Compare commits

...

58 Commits

Author SHA1 Message Date
b58d8d96ff Release 2.10.2 2023-08-08 02:54:23 +03:00
4abe3b8963 remove napoleon contrib dependency 2023-08-08 02:53:08 +03:00
237fec3f85 fix issues with remote pull triggers (see #103)
* The issue appears when repository contains PKGBUILD in root. In this
  case it will copy tree with loosing package information, because
  the repository will be cloned to temporary path with random generated
  name
* The issue appears when branch which is different from master is used
  for any reposittory with git files (e.g. single-pkgbuild repo or repo
  with submodules)
2023-08-08 02:34:44 +03:00
bd0f850d25 Release 2.10.1 2023-07-28 03:21:19 +03:00
5904727da2 do not create application in shell handler
The main reason for having shell handler is to be able to fix if
something (e.g. migrations) goes wrong. In this way we need to reduce
actions inside this wrapper
2023-07-28 03:06:28 +03:00
263c53bac5 rase 405 error in case if GET login method is used whereas no aioauth
library installed
2023-07-23 03:01:25 +03:00
6743f1d62a optimize schemas import 2023-07-23 02:24:39 +03:00
931ff9bd98 Release 2.10.0 2023-07-22 05:28:57 +03:00
6b3fc3a6a0 add support of table filter controls (#101) 2023-07-21 02:10:26 +03:00
b7852f55c8 remove unsafe flag from handlers
This flag became reduntant there and tree creation has been moved to
lock
2023-07-07 03:25:05 +03:00
721b447767 fix code block in docs 2023-07-06 19:17:11 +03:00
b80ea80e9d add salt generator to setup command instead 2023-07-06 19:16:49 +03:00
8e9da5baab register dependency package before build
If package has been added as dependency, the service miss remote as well
as causes some 400 errors in reporter
2023-07-06 03:11:19 +03:00
a443abb94e handle packages load from aur by package name also
In general package names array may not contain package base, thus it
leads to inability to load packages from aur by its base during update
process
2023-07-01 15:55:04 +03:00
61c565ab0d explicitly pass user agent for the arch linux sites requests 2023-06-26 02:52:08 +03:00
10100b20e1 print configuration paths in dump command 2023-06-11 15:11:34 +03:00
2922bb9d72 remove salt generation from users handler
It causes issues, because users handler is operating with service user,
but writtinng salt requires root privileges
2023-06-05 05:25:10 +03:00
17f5f41e36 complitely disable signature check for local repository in devtools
It appears that with optional level pacman still tries to validate the
key, which can lead to errors whille processing in docker container
2023-06-05 04:41:03 +03:00
c22ddd71d9 packager documentation update 2023-06-05 03:40:02 +03:00
4b984afb64 packagers support (#100) 2023-06-05 02:37:19 +03:00
d495163fdd use autodoc_mock_imports instead of custom mocking for docs 2023-05-31 19:17:16 +03:00
041e9536bf use cached property instead of custom __getattr__ implementation 2023-05-31 19:17:16 +03:00
1625fddccd support check dependencies 2023-05-31 19:17:16 +03:00
8f2c33fe5d mimic parent arguments during spawn process (#99) 2023-05-30 18:09:35 +03:00
d064e46af6 do not terminate child process explicitly
This feature sometimes causes the main process termination. Since the
child process has been already terminated (or going to) there is no need
to terminate it manually
2023-05-29 17:27:04 +03:00
5627c9cca0 fix case where it was impossible to create session if user and password
are required

Issue appears if auth is enabled and unix socket is disabled and caused
by 7f5e541120
2023-05-29 17:23:57 +03:00
8731cee8ea runtime logger handler selector 2023-05-29 03:41:11 +03:00
01a4d68ce6 add repod reference to faq 2023-05-26 12:58:25 +03:00
8f0a6cf6b5 allow to filter packages for rebuild by their statuses 2023-05-25 17:41:54 +03:00
d96e3c97db fix documentation error 2023-05-25 16:31:47 +03:00
1cf027cb55 Support type triggers (#96)
* implement mirrorlist package generator

* implement keyring package generator

* docs update

* do not skip empty lines

* fill remote source for local packages

* faq update
2023-05-25 16:27:16 +03:00
8e0e57e193 switch to explicit journald logger instead of syslog 2023-05-25 04:44:38 +03:00
f855e645cc update optional dependencies list 2023-05-24 15:39:36 +03:00
8ab0df2bed bump rtd image 2023-05-24 03:58:14 +03:00
840921f585 update packaging files
also remove makedependencies after installation in docker
2023-05-22 18:23:06 +03:00
6f566b0c6c Release 2.9.0 2023-05-22 17:04:32 +03:00
d06399db2c update models index 2023-05-22 17:02:55 +03:00
70e9e3e822 migrate to the latest devtools & archlinux git changes
This migration includes
* removal of community repository as it is no more
* fixed paths for devtools configurations
* migration of archlinux packaging git url
2023-05-22 16:51:27 +03:00
8c55438140 disable debugpanel for now 2023-05-04 14:30:27 +03:00
277d40d231 do not assign path in context manager as it is deprectated 2023-05-04 14:29:39 +03:00
2ff56965d9 PEP-673 use Self as return type for classmethods (#94)
* PEP-673 use Self as return type for classmethods

* add dummy test file

* remove python3.10 compat
2023-05-04 03:28:08 +03:00
9dc6d56a8d fix flapping tests for oauth
Original implementation sends requests to httpbin which sometimes might
not be available. With proposed changes we are blocking redirects and
just check request itself
2023-05-03 14:53:35 +03:00
d59a0629c5 PEP-561 complaint: add py.typed marker 2023-04-17 18:56:49 +03:00
31f47b8069 fix some pycharm warnings 2023-04-17 02:23:50 +03:00
e42ca95789 suppress status errors also if option is set 2023-04-15 05:05:55 +03:00
ff917281a2 add referencne to api docs to docs 2023-04-14 05:06:53 +03:00
f7e260e1f4 add reference to api documentation from main page 2023-04-12 04:22:49 +03:00
abf29083c4 PEP-585 complaint: remove type aliases (#93) 2023-04-12 04:09:28 +03:00
54d36ccf36 docs update 2023-04-10 21:13:43 +03:00
2cd61b0a20 Release 2.8.0 2023-04-09 13:06:54 +03:00
9503a9f2ae try to remove unknown packages from api 2023-04-06 18:14:36 +03:00
39fde7cd5f hide cookie key and salt from config output 2023-04-06 00:31:50 +03:00
58379e7bf3 optimize imports 2023-04-06 00:24:39 +03:00
3c068edf4f argument annootation update 2023-04-06 00:24:39 +03:00
1106ff6482 fix license url 2023-04-06 00:24:39 +03:00
e08ab2db10 extract schemas automatically from views 2023-04-06 00:24:39 +03:00
8f4a2547e8 use api generated docs instead of comments (#92) 2023-04-06 00:24:39 +03:00
7f5e541120 execute request in context methods instead of handling them each time
manually
2023-03-23 12:43:04 +02:00
369 changed files with 13529 additions and 6787 deletions

View File

@ -10,7 +10,7 @@ echo -e '[arcanisrepo]\nServer = http://repo.arcanis.me/$arch\nSigLevel = Never'
# refresh the image
pacman --noconfirm -Syu
# main dependencies
pacman --noconfirm -Sy base-devel devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-setuptools python-srcinfo sudo
pacman --noconfirm -Sy base-devel devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo python-systemd sudo
# make dependencies
pacman --noconfirm -Sy python-build python-installer python-wheel
# optional dependencies
@ -18,7 +18,7 @@ if [[ -z $MINIMAL_INSTALL ]]; then
# VCS support
pacman --noconfirm -Sy breezy darcs mercurial subversion
# web server
pacman --noconfirm -Sy python-aioauth-client python-aiohttp python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
pacman --noconfirm -Sy python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
# additional features
pacman --noconfirm -Sy gnupg python-boto3 rsync
fi

View File

@ -4,7 +4,7 @@
set -ex
# install dependencies
pacman --noconfirm -Syu base-devel python-pip python-setuptools python-tox
pacman --noconfirm -Syu base-devel python-setuptools python-tox
# run test and check targets
make check tests

View File

@ -6,7 +6,7 @@ formats:
build:
os: ubuntu-20.04
tools:
python: "3.10"
python: "3.11"
sphinx:
builder: html

View File

@ -34,7 +34,7 @@ Again, the most checks can be performed by `make check` command, though some add
do foo. With very very very long
docstring
Note:
Notes:
Very important note about this function
Args:
@ -90,7 +90,9 @@ Again, the most checks can be performed by `make check` command, though some add
self.instance_attribute = ""
```
* Type annotations are the must, even for local functions.
* Type annotations are the must, even for local functions. For the function argument `self` (for instance methods) and `cls` (for class methods) should not be annotated.
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`).
* `classmethod` should always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead.
* Recommended order of function definitions in class:
```python
@ -100,9 +102,12 @@ Again, the most checks can be performed by `make check` command, though some add
@property
def property(self) -> Any: ...
@cached_property
def property_cached(self) -> Any: ... # cached property has to be treated as normal one
@classmethod
def class_method(cls: Type[Clazz]) -> Clazz: ...
def class_method(cls) -> Self: ...
@staticmethod
def static_method() -> Any: ...
@ -156,6 +161,52 @@ Again, the most checks can be performed by `make check` command, though some add
* No global variable is allowed outside of `ahriman.version` module. `ahriman.core.context` is also special case.
* Single quotes are not allowed. The reason behind this restriction is the fact that docstrings must be written by using double quotes only, and we would like to make style consistent.
* If your class writes anything to log, the `ahriman.core.log.LazyLogging` trait must be used.
* Web API methods must be documented by using `aiohttp_apispec` library. Schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
```python
import aiohttp_apispec
from marshmallow import Schema, fields
from ahriman.web.schemas.auth_schema import AuthSchema
from ahriman.web.schemas.error_schema import ErrorSchema
from ahriman.web.schemas.package_name_schema import PackageNameSchema
from ahriman.web.views.base import BaseView
class RequestSchema(Schema):
field = fields.String(metadata={"description": "Field description", "example": "foo"})
class ResponseSchema(Schema):
field = fields.String(required=True, metadata={"description": "Field description"})
class Foo(BaseView):
POST_PERMISSION = ...
@aiohttp_apispec.docs(
tags=["Tag"],
summary="Do foo",
description="Extended description of the method which does foo",
responses={
200: {"description": "Success response", "schema": ResponseSchema},
204: {"description": "Success response"}, # example without json schema response
400: {"description": "Bad data is supplied", "schema": ErrorSchema}, # exception raised by this method
401: {"description": "Authorization required", "schema": ErrorSchema}, # should be always presented
403: {"description": "Access is forbidden", "schema": ErrorSchema}, # should be always presented
500: {"description": "Internal server error", "schema": ErrorSchema}, # should be always presented
},
security=[{"token": [POST_PERMISSION]}],
)
@aiohttp_apispec.cookies_schema(AuthSchema) # should be always presented
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.json_schema(RequestSchema(many=True))
async def post(self) -> None: ...
```
### Other checks

View File

@ -6,7 +6,7 @@ ENV AHRIMAN_DEBUG=""
ENV AHRIMAN_FORCE_ROOT=""
ENV AHRIMAN_HOST="0.0.0.0"
ENV AHRIMAN_MULTILIB="yes"
ENV AHRIMAN_OUTPUT="syslog"
ENV AHRIMAN_OUTPUT=""
ENV AHRIMAN_PACKAGER="ahriman bot <ahriman@example.com>"
ENV AHRIMAN_PACMAN_MIRROR=""
ENV AHRIMAN_PORT=""
@ -28,14 +28,11 @@ RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
RUN pacman --noconfirm -Sy devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-setuptools python-srcinfo && \
pacman --noconfirm -Sy python-build python-installer python-wheel && \
pacman --noconfirm -Sy breezy mercurial python-aiohttp python-boto3 python-cryptography python-jinja python-requests-unixsocket rsync subversion && \
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-jinja2 python-aiohttp-debugtoolbar \
python-aiohttp-session python-aiohttp-security
# cleanup unused
RUN find "/var/cache/pacman/pkg" -type f -delete
RUN pacman -Sy --noconfirm --asdeps devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo && \
pacman -Sy --noconfirm --asdeps python-build python-installer python-wheel && \
pacman -Sy --noconfirm --asdeps breezy mercurial python-aiohttp python-aiohttp-cors python-boto3 python-cryptography python-jinja python-requests-unixsocket python-systemd rsync subversion && \
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-apispec-git python-aiohttp-jinja2 \
python-aiohttp-debugtoolbar python-aiohttp-session python-aiohttp-security
# install ahriman
## copy tree
@ -48,6 +45,10 @@ RUN cd "/home/build/ahriman" && \
runuser -u build -- makepkg --noconfirm --install --skipchecksums && \
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

View File

@ -16,7 +16,7 @@ Wrapper for managing custom repository inspired by [repo-scripts](https://github
* VCS packages support.
* Official repository support.
* Ability to patch AUR packages and even create package from local PKGBUILDs.
* Sign support with gpg (repository, package, per package settings).
* Sign support with gpg (repository, package), multiple packagers support.
* Triggers for repository updates, e.g. synchronization to remote services (rsync, s3 and github) and report generation (email, html, telegram).
* Repository status interface with optional authorization and control options:
@ -36,6 +36,6 @@ The application provides reasonable defaults which allow to use it out-of-box; h
## Live demos
* [Build status page](https://ahriman-demo.arcanis.me). You can log in as `demo` user by using `demo` password. However, you will not be able to run tasks.
* [Build status page](https://ahriman-demo.arcanis.me). You can log in as `demo` user by using `demo` password. However, you will not be able to run tasks. [HTTP API documentation](https://ahriman-demo.arcanis.me/api-docs) is also available.
* [Repository index](http://repo.arcanis.me/x86_64/index.html).
* [Telegram feed](https://t.me/arcanisrepo).

View File

@ -15,15 +15,10 @@ database = $AHRIMAN_REPOSITORY_ROOT/ahriman.db
host = $AHRIMAN_HOST
EOF
sed -i "s|handlers = syslog_handler|handlers = ${AHRIMAN_OUTPUT}_handler|g" "/etc/ahriman.ini.d/logging.ini"
AHRIMAN_DEFAULT_ARGS=("--architecture" "$AHRIMAN_ARCHITECTURE")
if [[ "$AHRIMAN_OUTPUT" == "syslog" ]]; then
if [ ! -e "/dev/log" ]; then
# by default ahriman uses syslog which is not available inside container
# to make noise less we force quiet mode in case if /dev/log was not mounted
AHRIMAN_DEFAULT_ARGS+=("--quiet")
fi
if [ -n "$AHRIMAN_OUTPUT" ]; then
AHRIMAN_DEFAULT_ARGS+=("--log-handler" "$AHRIMAN_OUTPUT")
fi
# create repository root inside the [[mounted]] directory and set correct ownership
@ -63,7 +58,7 @@ systemd-machine-id-setup &> /dev/null
# otherwise we prepend executable by sudo command
if [ -n "$AHRIMAN_FORCE_ROOT" ]; then
AHRIMAN_EXECUTABLE=("ahriman")
elif ahriman help-commands-unsafe --command="$*" &> /dev/null; then
elif ahriman help-commands-unsafe -- "$@" &> /dev/null; then
AHRIMAN_EXECUTABLE=("sudo" "-u" "$AHRIMAN_USER" "--" "ahriman")
else
AHRIMAN_EXECUTABLE=("ahriman")

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 660 KiB

After

Width:  |  Height:  |  Size: 829 KiB

View File

@ -1,9 +1,9 @@
.TH AHRIMAN "1" "2023\-03\-06" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2023\-08\-07" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS
.B ahriman
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,help,help-commands-unsafe,help-updates,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,user-add,user-list,user-remove,web} ...
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,help,help-commands-unsafe,help-updates,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,user-add,user-list,user-remove,web} ...
.SH DESCRIPTION
ArcH linux ReposItory MANager
@ -24,9 +24,13 @@ force run, remove file lock
\fB\-l\fR \fI\,LOCK\/\fR, \fB\-\-lock\fR \fI\,LOCK\/\fR
lock file
.TP
\fB\-\-log\-handler\fR \fI\,{console,syslog,journald}\/\fR
explicit log handler specification. If none set, the handler will be guessed from environment
.TP
\fB\-\-report\fR, \fB\-\-no\-report\fR
force enable or disable reporting to web service (default: True)
force enable or disable reporting to web service
.TP
\fB\-q\fR, \fB\-\-quiet\fR
@ -91,6 +95,12 @@ backup repository data
\fBahriman\fR \fI\,repo\-check\/\fR
check for updates
.TP
\fBahriman\fR \fI\,repo\-create\-keyring\/\fR
create keyring package
.TP
\fBahriman\fR \fI\,repo\-create\-mirrorlist\/\fR
create mirrorlist package
.TP
\fBahriman\fR \fI\,repo\-daemon\/\fR
run application as daemon
.TP
@ -172,7 +182,7 @@ return non\-zero exit status if result is empty
.TP
\fB\-\-info\fR, \fB\-\-no\-info\fR
show additional package information (default: False)
show additional package information
.TP
\fB\-\-sort\-by\fR \fI\,{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}\/\fR
@ -189,13 +199,12 @@ show help message for application or command and exit
show help message for specific command
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
usage: ahriman help\-commands\-unsafe [\-h] [\-\-command COMMAND]
usage: ahriman help\-commands\-unsafe [\-h] [command ...]
list unsafe commands as defined in default args
.SH OPTIONS \fI\,'ahriman help\-commands\-unsafe'\/\fR
.TP
\fB\-\-command\fR \fI\,COMMAND\/\fR
\fBcommand\fR
instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1
otherwise
@ -216,7 +225,7 @@ print application and its dependencies versions
.SH COMMAND \fI\,'ahriman package\-add'\/\fR
usage: ahriman package\-add [\-h] [\-\-dependencies | \-\-no\-dependencies] [\-e] [\-n] [\-y]
[\-s {auto,archive,aur,directory,local,remote,repository}]
[\-s {auto,archive,aur,directory,local,remote,repository}] [\-u USERNAME]
package [package ...]
add existing or new package to the build queue
@ -228,7 +237,7 @@ package source (base name, path to local files, remote URL)
.SH OPTIONS \fI\,'ahriman package\-add'\/\fR
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies (default: True)
process missing package dependencies
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
@ -246,6 +255,10 @@ download fresh package databases from the mirror before actions, \-yy to force r
\fB\-s\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR, \fB\-\-source\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR
explicitly specify the package source for this command
.TP
\fB\-u\fR \fI\,USERNAME\/\fR, \fB\-\-username\fR \fI\,USERNAME\/\fR
build as user
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
usage: ahriman package\-remove [\-h] package [package ...]
@ -276,7 +289,7 @@ return non\-zero exit status if result is empty
.TP
\fB\-\-info\fR, \fB\-\-no\-info\fR
show additional package information (default: False)
show additional package information
.TP
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
@ -394,12 +407,22 @@ return non\-zero exit status if result is empty
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
fetch actual version of VCS packages (default: True)
fetch actual version of VCS packages
.TP
\fB\-y\fR, \fB\-\-refresh\fR
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
.SH COMMAND \fI\,'ahriman repo\-create\-keyring'\/\fR
usage: ahriman repo\-create\-keyring [\-h]
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
.SH COMMAND \fI\,'ahriman repo\-create\-mirrorlist'\/\fR
usage: ahriman repo\-create\-mirrorlist [\-h]
create package which contains list of available mirrors as set by configuration. Note, that this action will only create package, the package itself has to be built manually
.SH COMMAND \fI\,'ahriman repo\-daemon'\/\fR
usage: ahriman repo\-daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-dependencies | \-\-no\-dependencies]
[\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-\-vcs | \-\-no\-vcs] [\-y]
@ -413,23 +436,23 @@ interval between runs in seconds
.TP
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates (default: True)
enable or disable checking for AUR updates
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies (default: True)
process missing package dependencies
.TP
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates (default: True)
enable or disable checking of local packages for updates
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
include or exclude manual updates (default: True)
include or exclude manual updates
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
fetch actual version of VCS packages (default: True)
fetch actual version of VCS packages
.TP
\fB\-y\fR, \fB\-\-refresh\fR
@ -437,6 +460,7 @@ download fresh package databases from the mirror before actions, \-yy to force r
.SH COMMAND \fI\,'ahriman repo\-rebuild'\/\fR
usage: ahriman repo\-rebuild [\-h] [\-\-depends\-on DEPENDS_ON] [\-\-dry\-run] [\-\-from\-database] [\-e]
[\-s {unknown,pending,building,failed,success}] [\-u USERNAME]
force rebuild whole repository
@ -459,6 +483,14 @@ original ahriman instance run with web service and have run repo\-update at leas
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
filter packages by status. Requires \-\-from\-database to be set
.TP
\fB\-u\fR \fI\,USERNAME\/\fR, \fB\-\-username\fR \fI\,USERNAME\/\fR
build as user
.SH COMMAND \fI\,'ahriman repo\-remove\-unknown'\/\fR
usage: ahriman repo\-remove\-unknown [\-h] [\-\-dry\-run]
@ -528,7 +560,7 @@ instead of running all triggers as set by configuration, just process specified
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-e]
[\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-\-vcs | \-\-no\-vcs] [\-y]
[\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
[package ...]
check for packages updates and run build process if requested
@ -540,11 +572,11 @@ filter check by package base
.SH OPTIONS \fI\,'ahriman repo\-update'\/\fR
.TP
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates (default: True)
enable or disable checking for AUR updates
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies (default: True)
process missing package dependencies
.TP
\fB\-\-dry\-run\fR
@ -556,15 +588,19 @@ return non\-zero exit status if result is empty
.TP
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates (default: True)
enable or disable checking of local packages for updates
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
include or exclude manual updates (default: True)
include or exclude manual updates
.TP
\fB\-u\fR \fI\,USERNAME\/\fR, \fB\-\-username\fR \fI\,USERNAME\/\fR
build as user
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
fetch actual version of VCS packages (default: True)
fetch actual version of VCS packages
.TP
\fB\-y\fR, \fB\-\-refresh\fR
@ -579,23 +615,23 @@ remove local caches
.SH OPTIONS \fI\,'ahriman service\-clean'\/\fR
.TP
\fB\-\-cache\fR, \fB\-\-no\-cache\fR
clear directory with package caches (default: False)
clear directory with package caches
.TP
\fB\-\-chroot\fR, \fB\-\-no\-chroot\fR
clear build chroot (default: False)
clear build chroot
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
clear manually added packages queue (default: False)
clear manually added packages queue
.TP
\fB\-\-packages\fR, \fB\-\-no\-packages\fR
clear directory with built packages (default: False)
clear directory with built packages
.TP
\fB\-\-pacman\fR, \fB\-\-no\-pacman\fR
clear directory with pacman local database cache (default: False)
clear directory with pacman local database cache
.SH COMMAND \fI\,'ahriman service\-config'\/\fR
usage: ahriman service\-config [\-h] [\-\-secure | \-\-no\-secure]
@ -605,7 +641,7 @@ dump configuration for the specified architecture
.SH OPTIONS \fI\,'ahriman service\-config'\/\fR
.TP
\fB\-\-secure\fR, \fB\-\-no\-secure\fR
hide passwords and secrets from output (default: True)
hide passwords and secrets from output
.SH COMMAND \fI\,'ahriman service\-config\-validate'\/\fR
usage: ahriman service\-config\-validate [\-h] [\-e]
@ -633,9 +669,10 @@ key server for key import
.SH COMMAND \fI\,'ahriman service\-setup'\/\fR
usage: ahriman service\-setup [\-h] [\-\-build\-as\-user BUILD_AS_USER] [\-\-build\-command BUILD_COMMAND]
[\-\-from\-configuration FROM_CONFIGURATION] [\-\-makeflags\-jobs | \-\-no\-makeflags\-jobs]
[\-\-mirror MIRROR] [\-\-multilib | \-\-no\-multilib] \-\-packager PACKAGER \-\-repository REPOSITORY
[\-\-sign\-key SIGN_KEY] [\-\-sign\-target {disabled,packages,repository}] [\-\-web\-port WEB_PORT]
[\-\-from\-configuration FROM_CONFIGURATION] [\-\-generate\-salt | \-\-no\-generate\-salt]
[\-\-makeflags\-jobs | \-\-no\-makeflags\-jobs] [\-\-mirror MIRROR] [\-\-multilib | \-\-no\-multilib]
\-\-packager PACKAGER \-\-repository REPOSITORY [\-\-sign\-key SIGN_KEY]
[\-\-sign\-target {disabled,packages,repository}] [\-\-web\-port WEB_PORT]
[\-\-web\-unix\-socket WEB_UNIX_SOCKET]
create initial service configuration, requires root
@ -653,9 +690,13 @@ build command prefix
\fB\-\-from\-configuration\fR \fI\,FROM_CONFIGURATION\/\fR
path to default devtools pacman configuration
.TP
\fB\-\-generate\-salt\fR, \fB\-\-no\-generate\-salt\fR
generate salt for user passwords
.TP
\fB\-\-makeflags\-jobs\fR, \fB\-\-no\-makeflags\-jobs\fR
append MAKEFLAGS variable with parallelism set to number of cores (default: True)
append MAKEFLAGS variable with parallelism set to number of cores
.TP
\fB\-\-mirror\fR \fI\,MIRROR\/\fR
@ -663,7 +704,7 @@ use the specified explicitly mirror instead of including mirrorlist
.TP
\fB\-\-multilib\fR, \fB\-\-no\-multilib\fR
add or do not multilib repository (default: True)
add or do not multilib repository
.TP
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
@ -692,14 +733,15 @@ path to unix socket used for interprocess communications
.SH COMMAND \fI\,'ahriman service\-shell'\/\fR
usage: ahriman service\-shell [\-h] [code]
drop into python shell while having created application
drop into python shell
.TP
\fBcode\fR
instead of dropping into shell, just execute the specified code
.SH COMMAND \fI\,'ahriman user\-add'\/\fR
usage: ahriman user\-add [\-h] [\-p PASSWORD] [\-r {unauthorized,read,reporter,full}] [\-s] username
usage: ahriman user\-add [\-h] [\-\-key KEY] [\-\-packager PACKAGER] [\-p PASSWORD] [\-r {unauthorized,read,reporter,full}]
username
update user for web services with the given password and role. In case if password was not entered it will be asked interactively
@ -708,6 +750,14 @@ update user for web services with the given password and role. In case if passwo
username for web service
.SH OPTIONS \fI\,'ahriman user\-add'\/\fR
.TP
\fB\-\-key\fR \fI\,KEY\/\fR
optional PGP key used by this user. The private key must be imported
.TP
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
optional packager id used for build process in form of `Name Surname <mail@example.com>`
.TP
\fB\-p\fR \fI\,PASSWORD\/\fR, \fB\-\-password\fR \fI\,PASSWORD\/\fR
user password. Blank password will be treated as empty password, which is in particular must be used for OAuth2
@ -717,10 +767,6 @@ authorization type.
\fB\-r\fR \fI\,{unauthorized,read,reporter,full}\/\fR, \fB\-\-role\fR \fI\,{unauthorized,read,reporter,full}\/\fR
user access level
.TP
\fB\-s\fR, \fB\-\-secure\fR
set file permissions to user\-only
.SH COMMAND \fI\,'ahriman user\-list'\/\fR
usage: ahriman user\-list [\-h] [\-e] [\-r {unauthorized,read,reporter,full}] [username]
@ -756,7 +802,7 @@ start web server
.SH COMMENTS
Argument list can also be read from file by using @ prefix.
.SH AUTHORS
.SH AUTHOR
.nf
ahriman team
.fi

View File

@ -60,6 +60,22 @@ ahriman.core.database.migrations.m006\_packages\_architecture\_required module
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m007\_check\_depends module
------------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m007_check_depends
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m008\_packagers module
-------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m008_packagers
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -20,6 +20,14 @@ ahriman.core.formatters.build\_printer module
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.configuration\_paths\_printer module
------------------------------------------------------------
.. automodule:: ahriman.core.formatters.configuration_paths_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.configuration\_printer module
-----------------------------------------------------

View File

@ -20,6 +20,14 @@ ahriman.core.log.http\_log\_handler module
:no-undoc-members:
:show-inheritance:
ahriman.core.log.journal\_handler module
----------------------------------------
.. automodule:: ahriman.core.log.journal_handler
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.log.lazy\_logging module
-------------------------------------

View File

@ -19,6 +19,7 @@ Subpackages
ahriman.core.repository
ahriman.core.sign
ahriman.core.status
ahriman.core.support
ahriman.core.triggers
ahriman.core.upload

View File

@ -0,0 +1,37 @@
ahriman.core.support.pkgbuild package
=====================================
Submodules
----------
ahriman.core.support.pkgbuild.keyring\_generator module
-------------------------------------------------------
.. automodule:: ahriman.core.support.pkgbuild.keyring_generator
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.support.pkgbuild.mirrorlist\_generator module
----------------------------------------------------------
.. automodule:: ahriman.core.support.pkgbuild.mirrorlist_generator
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.support.pkgbuild.pkgbuild\_generator module
--------------------------------------------------------
.. automodule:: ahriman.core.support.pkgbuild.pkgbuild_generator
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.support.pkgbuild
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -0,0 +1,45 @@
ahriman.core.support package
============================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.core.support.pkgbuild
Submodules
----------
ahriman.core.support.keyring\_trigger module
--------------------------------------------
.. automodule:: ahriman.core.support.keyring_trigger
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.support.mirrorlist\_trigger module
-----------------------------------------------
.. automodule:: ahriman.core.support.mirrorlist_trigger
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.support.package\_creator module
--------------------------------------------
.. automodule:: ahriman.core.support.package_creator
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.support
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -60,6 +60,14 @@ ahriman.models.internal\_status module
:no-undoc-members:
:show-inheritance:
ahriman.models.log\_handler module
----------------------------------
.. automodule:: ahriman.models.log_handler
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.log\_record\_id module
-------------------------------------
@ -108,6 +116,22 @@ ahriman.models.package\_source module
:no-undoc-members:
:show-inheritance:
ahriman.models.packagers module
-------------------------------
.. automodule:: ahriman.models.packagers
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.pacman\_synchronization module
---------------------------------------------
.. automodule:: ahriman.models.pacman_synchronization
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.pkgbuild\_patch module
-------------------------------------

View File

@ -8,11 +8,28 @@ Subpackages
:maxdepth: 4
ahriman.web.middlewares
ahriman.web.schemas
ahriman.web.views
Submodules
----------
ahriman.web.apispec module
--------------------------
.. automodule:: ahriman.web.apispec
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.cors module
-----------------------
.. automodule:: ahriman.web.cors
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.routes module
-------------------------

View File

@ -0,0 +1,165 @@
ahriman.web.schemas package
===========================
Submodules
----------
ahriman.web.schemas.aur\_package\_schema module
-----------------------------------------------
.. automodule:: ahriman.web.schemas.aur_package_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.auth\_schema module
---------------------------------------
.. automodule:: ahriman.web.schemas.auth_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.counters\_schema module
-------------------------------------------
.. automodule:: ahriman.web.schemas.counters_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.error\_schema module
----------------------------------------
.. automodule:: ahriman.web.schemas.error_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.internal\_status\_schema module
---------------------------------------------------
.. automodule:: ahriman.web.schemas.internal_status_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.log\_schema module
--------------------------------------
.. automodule:: ahriman.web.schemas.log_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.login\_schema module
----------------------------------------
.. automodule:: ahriman.web.schemas.login_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.logs\_schema module
---------------------------------------
.. automodule:: ahriman.web.schemas.logs_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.oauth2\_schema module
-----------------------------------------
.. automodule:: ahriman.web.schemas.oauth2_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_name\_schema module
------------------------------------------------
.. automodule:: ahriman.web.schemas.package_name_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_names\_schema module
-------------------------------------------------
.. automodule:: ahriman.web.schemas.package_names_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_properties\_schema module
------------------------------------------------------
.. automodule:: ahriman.web.schemas.package_properties_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_schema module
------------------------------------------
.. automodule:: ahriman.web.schemas.package_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_status\_schema module
--------------------------------------------------
.. automodule:: ahriman.web.schemas.package_status_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.pgp\_key\_id\_schema module
-----------------------------------------------
.. automodule:: ahriman.web.schemas.pgp_key_id_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.pgp\_key\_schema module
-------------------------------------------
.. automodule:: ahriman.web.schemas.pgp_key_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.remote\_schema module
-----------------------------------------
.. automodule:: ahriman.web.schemas.remote_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.search\_schema module
-----------------------------------------
.. automodule:: ahriman.web.schemas.search_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.status\_schema module
-----------------------------------------
.. automodule:: ahriman.web.schemas.status_schema
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.schemas
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -0,0 +1,29 @@
ahriman.web.views.api package
=============================
Submodules
----------
ahriman.web.views.api.docs module
---------------------------------
.. automodule:: ahriman.web.views.api.docs
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.api.swagger module
------------------------------------
.. automodule:: ahriman.web.views.api.swagger
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.api
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -7,6 +7,7 @@ Subpackages
.. toctree::
:maxdepth: 4
ahriman.web.views.api
ahriman.web.views.service
ahriman.web.views.status
ahriman.web.views.user

View File

@ -33,6 +33,7 @@ This package contains everything required for the most of application actions an
* ``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).
* ``ahriman.core.auth`` package provides classes for authorization methods used by web mostly. Base class is ``ahriman.core.auth.Auth`` which must be called by ``load`` method.
* ``ahriman.core.build_tools`` is a package which provides wrapper for ``devtools`` commands.
* ``ahriman.core.configuration`` contains extension for standard ``configparser`` library and some validation related classes.
* ``ahriman.core.database`` is everything including data and schema migrations for database.
* ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers.
* ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly.
@ -41,12 +42,12 @@ This package contains everything required for the most of application actions an
* ``ahriman.core.repository`` contains several traits and base repository (``ahriman.core.repository.Repository`` class) implementation.
* ``ahriman.core.sign`` package provides sign feature (only gpg calls are available).
* ``ahriman.core.status`` contains helpers and watcher class which are required for web application. Reporter must be initialized by using ``ahriman.core.status.client.Client.load`` method.
* ``ahriman.core.support`` provides plugins for support packages (mirrorlist and keyring) generation.
* ``ahriman.core.triggers`` package contains base trigger classes. Classes from this package must be imported in order to implement user extensions. In fact, ``ahriman.core.report`` and ``ahriman.core.upload`` use this package.
* ``ahriman.core.upload`` package provides sync feature, should not be called directly.
This package also provides some generic functions and classes which may be used by other packages:
* ``ahriman.core.configuration.Configuration`` is an extension for standard ``configparser`` library.
* ``ahriman.core.exceptions`` provides custom exceptions.
* ``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.
@ -62,20 +63,23 @@ It provides models for any other part of application. Unlike ``ahriman.core`` pa
Web application. It is important that this package is isolated from any other to allow it to be optional feature (i.e. dependencies which are required by the package are optional).
* ``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.
* ``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.
* ``ahriman.web.web`` provides main web application functions (e.g. start, initialization).
Application run
---------------
* Parse command line arguments, find command and related handler which is set by 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 process in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether 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.
* Some handlers may override their status and throw ``ExitCode`` exception. This exception is just silently suppressed and changes application exit code to ``1``.
#. Parse command line arguments, find command and related handler which is set by 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 process in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether 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.
#. Some handlers may override their status and throw ``ExitCode`` exception. This exception is just silently suppressed and changes application exit code to ``1``.
In the most cases handlers spawn god class ``ahriman.application.application.Application`` class and call required methods.
@ -114,18 +118,24 @@ Schema and data migrations
The schema migration are applied according to current ``pragma user_info`` values, located at ``ahriman.core.database.migrations`` package and named as ``m000_migration_name.py`` (the preceding ``m`` is required in order to import migration content for tests). Additional class ``ahriman.core.database.migrations.Migrations`` reads all migrations automatically and applies them in alphabetical order.
These migrations also contain data migrations. Though the recommended way is to migrate data directly from SQL requests, sometimes it is required to have external data (like packages list) in order to set correct data. To do so, special method `migrate_data` is used.
These migrations can also contain data migrations. Though the recommended way is to migrate data directly from SQL requests, sometimes it is required to have external data (like packages list) in order to set correct data. To do so, special method `migrate_data` is used.
Type conversions
^^^^^^^^^^^^^^^^
By default, it parses rows into python dictionary. In addition, the following pseudo-types are supported:
* ``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.
* ``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. ``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 (sudo configuration required).
Add new packages or rebuild existing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -140,7 +150,7 @@ This logic can be overwritten by specifying the ``source`` parameter, which is p
Rebuild packages
^^^^^^^^^^^^^^^^
Same as add function for every package in repository. Optional filter by reverse dependency can be supplied.
Same as add function for every package in repository. Optional filters by reverse dependency or build status can be supplied.
Remove packages
^^^^^^^^^^^^^^^
@ -224,7 +234,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 furher 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 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 commands.
@ -235,7 +245,7 @@ Triggers are extensions which can be used in order to perform any actions on app
The main idea is to load classes by their full path (e.g. ``ahriman.core.upload.UploadTrigger``) by using ``importlib``: get the last part of the import and treat it as class name, join remain part by ``.`` and interpret as module path, import module and extract attribute from it.
The loaded triggers will be called with ``ahriman.models.result.Result`` and ``List[Packages]`` arguments, which describes the process result and current repository packages respectively. Any exception raised will be suppressed and will generate an exception message in logs.
The loaded triggers will be called with ``ahriman.models.result.Result`` and ``list[Packages]`` arguments, which describes the process result and current repository packages respectively. Any exception raised will be suppressed and will generate an exception message in logs.
In addition triggers can implement ``on_start`` and ``on_stop`` actions which will be called on the application start and right before the application exit. The ``on_start`` action is usually being called from handlers directly in order to make sure that no trigger will be run when it is not required (e.g. on user management). As soon as ``on_start`` action is called, the additional flag will be set; ``ahriman.core.triggers.TriggerLoader`` class implements ``__del__`` method in which, if the flag is set, the ``on_stop`` actions will be called.
@ -269,6 +279,7 @@ Web application
Web application requires the following python packages to be installed:
* Core part requires ``aiohttp`` (application itself), ``aiohttp_jinja2`` and ``Jinja2`` (HTML generation from templates).
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation), ``aiohttp_cors`` (CORS support, required by documentation)
* In addition, ``aiohttp_debugtoolbar`` is required for debug panel. Please note that this option does not work together with authorization and basically must not be used in production.
* In addition, authorization feature requires ``aiohttp_security``, ``aiohttp_session`` and ``cryptography``.
* In addition to base authorization dependencies, OAuth2 also requires ``aioauth-client`` library.
@ -279,6 +290,13 @@ Middlewares
Service provides some custom middlewares, e.g. logging every exception (except for user ones) and user authorization.
HEAD and OPTIONS requests
^^^^^^^^^^^^^^^^^^^^^^^^^
``HEAD`` request is automatically generated by ``ahriman.web.views.base.BaseView`` class. It just calls ``GET`` method, removes any data from body and returns the result. In case if no ``GET`` method available for this view, the ``aiohttp.web.HTTPMethodNotAllowed`` exception will be raised.
On the other side, ``OPTIONS`` method is implemented in the ``ahriman.web.middlewares.exception_handler.exception_handler`` middleware. In case if ``aiohttp.web.HTTPMethodNotAllowed`` exception is raised and original method was ``OPTIONS``, the middleware handles it, converts to valid request and returns response to user.
Web views
^^^^^^^^^
@ -288,6 +306,7 @@ REST API supports both form and JSON data, but the last one is recommended.
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.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.
@ -310,3 +329,5 @@ External calls
^^^^^^^^^^^^^^
Web application provides external calls to control main service. It spawns child process with specific arguments and waits for its termination. This feature must be used either with authorization or in safe (i.e. when status page is not available world-wide) environment.
For most actions it also extracts user from authentication (if provided) and passes it to underlying process.

View File

@ -1,18 +1,18 @@
# AUTOMATICALLY GENERATED by `shtab`
_shtab_ahriman_subparsers=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_subparsers=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--report' '--no-report' '-q' '--quiet' '--unsafe' '-V' '--version')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '--report' '--no-report' '-q' '--quiet' '--unsafe' '-V' '--version')
_shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_help_option_strings=('-h' '--help')
_shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help' '--command')
_shtab_ahriman_help_commands_unsafe_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_version_option_strings=('-h' '--help')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source')
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username')
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username')
_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')
@ -27,10 +27,12 @@ _shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
_shtab_ahriman_repo_backup_option_strings=('-h' '--help')
_shtab_ahriman_repo_check_option_strings=('-h' '--help' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_check_option_strings=('-h' '--help' '-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' '--dependencies' '--no-dependencies' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '-e' '--exit-code')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '-e' '--exit-code')
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '-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')
@ -43,8 +45,8 @@ _shtab_ahriman_repo_sync_option_strings=('-h' '--help')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_repo_tree_option_strings=('-h' '--help')
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--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')
@ -56,21 +58,22 @@ _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_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--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_user_add_option_strings=('-h' '--help' '-p' '--password' '-r' '--role' '-s' '--secure')
_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_web_option_strings=('-h' '--help')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman___log_handler_choices=('console' 'syslog' 'journald')
_shtab_ahriman_aur_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
_shtab_ahriman_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')
@ -87,6 +90,10 @@ _shtab_ahriman_package_status_update__s_choices=('unknown' 'pending' 'building'
_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_rebuild__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_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_service_setup___sign_target_choices=('disabled' 'packages' 'repository')
@ -126,6 +133,7 @@ _shtab_ahriman_search___info_nargs=0
_shtab_ahriman_search___no_info_nargs=0
_shtab_ahriman_help__h_nargs=0
_shtab_ahriman_help___help_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_updates__h_nargs=0
@ -230,6 +238,10 @@ _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
_shtab_ahriman_repo_create_mirrorlist___help_nargs=0
_shtab_ahriman_repo_daemon__h_nargs=0
_shtab_ahriman_repo_daemon___help_nargs=0
_shtab_ahriman_repo_daemon___aur_nargs=0
@ -401,30 +413,40 @@ _shtab_ahriman_key_import__h_nargs=0
_shtab_ahriman_key_import___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
@ -439,8 +461,6 @@ _shtab_ahriman_shell__v_nargs=0
_shtab_ahriman_shell___verbose_nargs=0
_shtab_ahriman_user_add__h_nargs=0
_shtab_ahriman_user_add___help_nargs=0
_shtab_ahriman_user_add__s_nargs=0
_shtab_ahriman_user_add___secure_nargs=0
_shtab_ahriman_user_list__h_nargs=0
_shtab_ahriman_user_list___help_nargs=0
_shtab_ahriman_user_list__e_nargs=0
@ -469,7 +489,7 @@ _shtab_replace_nonword() {
# set default values (called for the initial parser & any subparsers)
_set_parser_defaults() {
local subparsers_var="${prefix}_subparsers[@]"
sub_parsers=${!subparsers_var}
sub_parsers=${!subparsers_var-}
local current_option_strings_var="${prefix}_option_strings[@]"
current_option_strings=${!current_option_strings_var}
@ -486,13 +506,13 @@ _set_new_action() {
current_action="${prefix}_$(_shtab_replace_nonword $1)"
local current_action_compgen_var=${current_action}_COMPGEN
current_action_compgen="${!current_action_compgen_var}"
current_action_compgen="${!current_action_compgen_var-}"
local current_action_choices_var="${current_action}_choices[@]"
current_action_choices="${!current_action_choices_var}"
current_action_choices="${!current_action_choices_var-}"
local current_action_nargs_var="${current_action}_nargs"
if [ -n "${!current_action_nargs_var}" ]; then
if [ -n "${!current_action_nargs_var-}" ]; then
current_action_nargs="${!current_action_nargs_var}"
else
current_action_nargs=1
@ -514,8 +534,8 @@ _shtab_ahriman() {
local completing_word="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=()
prefix=_shtab_ahriman
word_index=0
local prefix=_shtab_ahriman
local word_index=0
_set_parser_defaults
word_index=1
@ -524,13 +544,13 @@ _shtab_ahriman() {
while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}"
if [[ -n $sub_parsers && " ${sub_parsers[@]} " =~ " ${this_word} " ]]; then
if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ " ${current_option_strings[@]} " =~ " ${this_word} " ]]; then
if [[ " ${current_option_strings[@]} " == *" ${this_word} "* ]]; then
# a new action should be acquired (due to recognised option string or
# no more input expected from current action);
# the next positional action can fill in here

View File

@ -36,6 +36,8 @@ _shtab_ahriman_commands() {
"repo-clean:remove local caches"
"repo-config:dump configuration for the specified architecture"
"repo-config-validate:validate configuration and print found errors"
"repo-create-keyring: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"
"repo-create-mirrorlist: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"
"repo-daemon:start process which periodically will run update process"
"repo-init:create initial service configuration, requires root"
"repo-rebuild:force rebuild whole repository"
@ -56,9 +58,9 @@ _shtab_ahriman_commands() {
"service-config-validate:validate configuration and print found errors"
"service-key-import:import PGP key from public sources to the repository user"
"service-setup:create initial service configuration, requires root"
"service-shell:drop into python shell while having created application"
"service-shell:drop into python shell"
"setup:create initial service configuration, requires root"
"shell:drop into python shell while having created application"
"shell:drop into python shell"
"sign:(re-)sign packages and repository database according to current settings"
"status:request status of the package"
"status-update:update package status on the status page"
@ -75,85 +77,87 @@ _shtab_ahriman_commands() {
_shtab_ahriman_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-a,--architecture}"[target architectures. For several subcommands it can be used multiple times]:architecture:"
{-c,--configuration}"[configuration path]:configuration:"
"--force[force run, remove file lock]"
{-l,--lock}"[lock file]:lock:"
{--report,--no-report}"[force enable or disable reporting to web service (default\: \%(default)s)]:report:"
{-q,--quiet}"[force disable any logging]"
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable]"
"*"{-a,--architecture}"[target architectures. For several subcommands it can be used multiple times (default\: None)]:architecture:"
{-c,--configuration}"[configuration path (default\: \/etc\/ahriman.ini)]:configuration:"
"--force[force run, remove file lock (default\: False)]"
{-l,--lock}"[lock file (default\: \/tmp\/ahriman.lock)]:lock:"
"--log-handler[explicit log handler specification. If none set, the handler will be guessed from environment (default\: None)]:log_handler:(console syslog journald)"
{--report,--no-report}"[force enable or disable reporting to web service (default\: True)]:report:"
{-q,--quiet}"[force disable any logging (default\: False)]"
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable (default\: False)]"
"(- : *)"{-V,--version}"[show program\'s version number and exit]"
)
_shtab_ahriman_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-n,--now}"[run update function after]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-s,--source}"[explicitly specify the package source for this command]:source:(auto archive aur directory local remote repository)"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{-n,--now}"[run update function after (default\: False)]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
{-s,--source}"[explicitly specify the package source for this command (default\: PackageSource.Auto)]:source:(auto archive aur directory local remote repository)"
{-u,--username}"[build as user (default\: None)]:username:"
"(*):package source (base name, path to local files, remote URL):"
)
_shtab_ahriman_aur_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
"--sort-by[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]:sort_by:(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)"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--info,--no-info}"[show additional package information (default\: False)]:info:"
"--sort-by[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 (default\: name)]:sort_by:(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)"
"(*):search terms, can be specified multiple times, the result will match all terms:"
)
_shtab_ahriman_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
"(*)::filter check by package base (default\: None):"
)
_shtab_ahriman_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: \%(default)s)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: \%(default)s)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: \%(default)s)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: \%(default)s)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: \%(default)s)]:pacman:"
{--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: False)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: False)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: False)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:"
)
_shtab_ahriman_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: \%(default)s)]:secure:"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: True)]:secure:"
)
_shtab_ahriman_config_validate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
)
_shtab_ahriman_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: \%(default)s)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
)
_shtab_ahriman_help_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":show help message for specific command:"
":show help message for specific command (default\: None):"
)
_shtab_ahriman_help_commands_unsafe_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--command[instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise]:command:"
"(*)::instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise (default\: None):"
)
_shtab_ahriman_help_updates_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit code if updates available]"
{-e,--exit-code}"[return non-zero exit code if updates available (default\: False)]"
)
_shtab_ahriman_help_version_options=(
@ -162,33 +166,35 @@ _shtab_ahriman_help_version_options=(
_shtab_ahriman_init_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
"--build-command[build command prefix (default\: ahriman)]:build_command:"
"--from-configuration[path to default devtools pacman configuration (default\: \/usr\/share\/devtools\/pacman.conf.d\/extra.conf)]:from_configuration:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords (default\: False)]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: True)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist (default\: None)]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:"
"--packager[packager name and email (default\: None)]:packager:"
"--repository[repository name (default\: None)]:repository:"
"--sign-key[sign key id (default\: None)]:sign_key:"
"*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)"
"--web-port[port of the web service (default\: None)]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
)
_shtab_ahriman_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import]:key_server:"
"--key-server[key server for key import (default\: keyserver.ubuntu.com)]:key_server:"
":PGP key to import from public server:"
)
_shtab_ahriman_package_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-n,--now}"[run update function after]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-s,--source}"[explicitly specify the package source for this command]:source:(auto archive aur directory local remote repository)"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{-n,--now}"[run update function after (default\: False)]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
{-s,--source}"[explicitly specify the package source for this command (default\: PackageSource.Auto)]:source:(auto archive aur directory local remote repository)"
{-u,--username}"[build as user (default\: None)]:username:"
"(*):package source (base name, path to local files, remote URL):"
)
@ -199,11 +205,11 @@ _shtab_ahriman_package_remove_options=(
_shtab_ahriman_package_status_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--ahriman[get service status itself]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
{-s,--status}"[filter packages by status]:status:(unknown pending building failed success)"
"(*)::filter status by package base:"
"--ahriman[get service status itself (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--info,--no-info}"[show additional package information (default\: False)]:info:"
{-s,--status}"[filter packages by status (default\: None)]:status:(unknown pending building failed success)"
"(*)::filter status by package base (default\: None):"
)
_shtab_ahriman_package_status_remove_options=(
@ -213,17 +219,18 @@ _shtab_ahriman_package_status_remove_options=(
_shtab_ahriman_package_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated:"
{-s,--status}"[new package build status (default\: BuildStatusEnum.Success)]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):"
)
_shtab_ahriman_package_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-n,--now}"[run update function after]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-s,--source}"[explicitly specify the package source for this command]:source:(auto archive aur directory local remote repository)"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{-n,--now}"[run update function after (default\: False)]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
{-s,--source}"[explicitly specify the package source for this command (default\: PackageSource.Auto)]:source:(auto archive aur directory local remote repository)"
{-u,--username}"[build as user (default\: None)]:username:"
"(*):package source (base name, path to local files, remote URL):"
)
@ -231,34 +238,36 @@ _shtab_ahriman_patch_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":package base:"
":PKGBUILD variable or function name. If variable is a function, it must end with ():"
":path to file which contains function or variable value. If not set, the value will be read from stdin:"
":path to file which contains function or variable value. If not set, the value will be read from stdin (default\: None):"
)
_shtab_ahriman_patch_list_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
"*"{-v,--variable}"[if set, show only patches for specified PKGBUILD variables]:variable:"
":package base:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
"*"{-v,--variable}"[if set, show only patches for specified PKGBUILD variables (default\: None)]:variable:"
":package base (default\: None):"
)
_shtab_ahriman_patch_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-v,--variable}"[should be used for single-function patches in case if you wold like to remove only specified PKGBUILD variables. In case if not set, it will remove all patches related to the package]:variable:"
"*"{-v,--variable}"[should be used for single-function patches in case if you wold like to remove only specified PKGBUILD variables. In case if not set, it will remove all patches related to the package (default\: None)]:variable:"
":package base:"
)
_shtab_ahriman_patch_set_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-t,--track}"[files which has to be tracked]:track:"
"*"{-t,--track}"[files which has to be tracked (default\: \[\'\*.diff\', \'\*.patch\'\])]:track:"
":path to directory with changed files for patch addition\/update:"
)
_shtab_ahriman_rebuild_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*--depends-on[only rebuild packages that depend on specified packages]:depends_on:"
"--dry-run[just perform check for packages without rebuild process itself]"
"--from-database[read packages from database instead of filesystem. This feature in particular is required in case if you would like to restore repository from another repository instance. Note, however, that in order to restore packages you need to have original ahriman instance run with web service and have run repo-update at least once.]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
"*--depends-on[only rebuild packages that depend on specified packages (default\: None)]:depends_on:"
"--dry-run[just perform check for packages without rebuild process itself (default\: False)]"
"--from-database[read packages from database instead of filesystem. This feature in particular is required in case if you would like to restore repository from another repository instance. Note, however, that in order to restore packages you need to have original ahriman instance run with web service and have run repo-update at least once. (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{-s,--status}"[filter packages by status. Requires --from-database to be set (default\: None)]:status:(unknown pending building failed success)"
{-u,--username}"[build as user (default\: None)]:username:"
)
_shtab_ahriman_remove_options=(
@ -268,7 +277,7 @@ _shtab_ahriman_remove_options=(
_shtab_ahriman_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal]"
"--dry-run[just perform check for packages without removal (default\: False)]"
)
_shtab_ahriman_repo_backup_options=(
@ -278,69 +287,80 @@ _shtab_ahriman_repo_backup_options=(
_shtab_ahriman_repo_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
"(*)::filter check by package base (default\: None):"
)
_shtab_ahriman_repo_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: \%(default)s)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: \%(default)s)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: \%(default)s)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: \%(default)s)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: \%(default)s)]:pacman:"
{--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: False)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: False)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: False)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:"
)
_shtab_ahriman_repo_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: \%(default)s)]:secure:"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: True)]:secure:"
)
_shtab_ahriman_repo_config_validate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
)
_shtab_ahriman_repo_create_keyring_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_create_mirrorlist_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: \%(default)s)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
)
_shtab_ahriman_repo_init_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
"--build-command[build command prefix (default\: ahriman)]:build_command:"
"--from-configuration[path to default devtools pacman configuration (default\: \/usr\/share\/devtools\/pacman.conf.d\/extra.conf)]:from_configuration:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords (default\: False)]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: True)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist (default\: None)]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:"
"--packager[packager name and email (default\: None)]:packager:"
"--repository[repository name (default\: None)]:repository:"
"--sign-key[sign key id (default\: None)]:sign_key:"
"*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)"
"--web-port[port of the web service (default\: None)]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
)
_shtab_ahriman_repo_rebuild_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*--depends-on[only rebuild packages that depend on specified packages]:depends_on:"
"--dry-run[just perform check for packages without rebuild process itself]"
"--from-database[read packages from database instead of filesystem. This feature in particular is required in case if you would like to restore repository from another repository instance. Note, however, that in order to restore packages you need to have original ahriman instance run with web service and have run repo-update at least once.]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
"*--depends-on[only rebuild packages that depend on specified packages (default\: None)]:depends_on:"
"--dry-run[just perform check for packages without rebuild process itself (default\: False)]"
"--from-database[read packages from database instead of filesystem. This feature in particular is required in case if you would like to restore repository from another repository instance. Note, however, that in order to restore packages you need to have original ahriman instance run with web service and have run repo-update at least once. (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{-s,--status}"[filter packages by status. Requires --from-database to be set (default\: None)]:status:(unknown pending building failed success)"
{-u,--username}"[build as user (default\: None)]:username:"
)
_shtab_ahriman_repo_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal]"
"--dry-run[just perform check for packages without removal (default\: False)]"
)
_shtab_ahriman_repo_report_options=(
@ -349,34 +369,35 @@ _shtab_ahriman_repo_report_options=(
_shtab_ahriman_repo_restore_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-o,--output}"[root path of the extracted files]:output:"
{-o,--output}"[root path of the extracted files (default\: \/)]:output:"
":path of the input archive:"
)
_shtab_ahriman_repo_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
"--build-command[build command prefix (default\: ahriman)]:build_command:"
"--from-configuration[path to default devtools pacman configuration (default\: \/usr\/share\/devtools\/pacman.conf.d\/extra.conf)]:from_configuration:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords (default\: False)]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: True)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist (default\: None)]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:"
"--packager[packager name and email (default\: None)]:packager:"
"--repository[repository name (default\: None)]:repository:"
"--sign-key[sign key id (default\: None)]:sign_key:"
"*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)"
"--web-port[port of the web service (default\: None)]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
)
_shtab_ahriman_repo_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages:"
"(*)::sign only specified packages (default\: None):"
)
_shtab_ahriman_repo_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new status]:status:(unknown pending building failed success)"
{-s,--status}"[new status (default\: BuildStatusEnum.Success)]:status:(unknown pending building failed success)"
)
_shtab_ahriman_repo_sync_options=(
@ -389,20 +410,21 @@ _shtab_ahriman_repo_tree_options=(
_shtab_ahriman_repo_triggers_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::instead of running all triggers as set by configuration, just process specified ones in order of mention:"
"(*)::instead of running all triggers as set by configuration, just process specified ones in order of mention (default\: None):"
)
_shtab_ahriman_repo_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: \%(default)s)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
"--dry-run[just perform check for updates, same as check command]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
{-u,--username}"[build as user (default\: None)]:username:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
"(*)::filter check by package base (default\: None):"
)
_shtab_ahriman_report_options=(
@ -411,97 +433,99 @@ _shtab_ahriman_report_options=(
_shtab_ahriman_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
"--sort-by[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]:sort_by:(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)"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--info,--no-info}"[show additional package information (default\: False)]:info:"
"--sort-by[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 (default\: name)]:sort_by:(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)"
"(*):search terms, can be specified multiple times, the result will match all terms:"
)
_shtab_ahriman_service_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: \%(default)s)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: \%(default)s)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: \%(default)s)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: \%(default)s)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: \%(default)s)]:pacman:"
{--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: False)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: False)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: False)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:"
)
_shtab_ahriman_service_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: \%(default)s)]:secure:"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: True)]:secure:"
)
_shtab_ahriman_service_config_validate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
)
_shtab_ahriman_service_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import]:key_server:"
"--key-server[key server for key import (default\: keyserver.ubuntu.com)]:key_server:"
":PGP key to import from public server:"
)
_shtab_ahriman_service_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
"--build-command[build command prefix (default\: ahriman)]:build_command:"
"--from-configuration[path to default devtools pacman configuration (default\: \/usr\/share\/devtools\/pacman.conf.d\/extra.conf)]:from_configuration:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords (default\: False)]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: True)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist (default\: None)]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:"
"--packager[packager name and email (default\: None)]:packager:"
"--repository[repository name (default\: None)]:repository:"
"--sign-key[sign key id (default\: None)]:sign_key:"
"*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)"
"--web-port[port of the web service (default\: None)]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
)
_shtab_ahriman_service_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":instead of dropping into shell, just execute the specified code:"
":instead of dropping into shell, just execute the specified code (default\: None):"
)
_shtab_ahriman_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
"--build-command[build command prefix (default\: ahriman)]:build_command:"
"--from-configuration[path to default devtools pacman configuration (default\: \/usr\/share\/devtools\/pacman.conf.d\/extra.conf)]:from_configuration:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords (default\: False)]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: True)]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist (default\: None)]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:"
"--packager[packager name and email (default\: None)]:packager:"
"--repository[repository name (default\: None)]:repository:"
"--sign-key[sign key id (default\: None)]:sign_key:"
"*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)"
"--web-port[port of the web service (default\: None)]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
)
_shtab_ahriman_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":instead of dropping into shell, just execute the specified code:"
":instead of dropping into shell, just execute the specified code (default\: None):"
)
_shtab_ahriman_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages:"
"(*)::sign only specified packages (default\: None):"
)
_shtab_ahriman_status_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--ahriman[get service status itself]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
{-s,--status}"[filter packages by status]:status:(unknown pending building failed success)"
"(*)::filter status by package base:"
"--ahriman[get service status itself (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--info,--no-info}"[show additional package information (default\: False)]:info:"
{-s,--status}"[filter packages by status (default\: None)]:status:(unknown pending building failed success)"
"(*)::filter status by package base (default\: None):"
)
_shtab_ahriman_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated:"
{-s,--status}"[new package build status (default\: BuildStatusEnum.Success)]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):"
)
_shtab_ahriman_sync_options=(
@ -510,30 +534,32 @@ _shtab_ahriman_sync_options=(
_shtab_ahriman_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: \%(default)s)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: \%(default)s)]:dependencies:"
"--dry-run[just perform check for updates, same as check command]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
{-u,--username}"[build as user (default\: None)]:username:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
"(*)::filter check by package base (default\: None):"
)
_shtab_ahriman_user_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-p,--password}"[user password. Blank password will be treated as empty password, which is in particular must be used for OAuth2 authorization type.]:password:"
{-r,--role}"[user access level]:role:(unauthorized read reporter full)"
{-s,--secure}"[set file permissions to user-only]"
"--key[optional PGP key used by this user. The private key must be imported (default\: None)]:key:"
"--packager[optional packager id used for build process in form of \`Name Surname \<mail\@example.com\>\` (default\: None)]:packager:"
{-p,--password}"[user password. Blank password will be treated as empty password, which is in particular must be used for OAuth2 authorization type. (default\: None)]:password:"
{-r,--role}"[user access level (default\: UserAccess.Read)]:role:(unauthorized read reporter full)"
":username for web service:"
)
_shtab_ahriman_user_list_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-r,--role}"[filter users by role]:role:(unauthorized read reporter full)"
":filter users by username:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{-r,--role}"[filter users by role (default\: None)]:role:(unauthorized read reporter full)"
":filter users by username (default\: None):"
)
_shtab_ahriman_user_remove_options=(
@ -556,7 +582,7 @@ _shtab_ahriman() {
if ((${_shtab_ahriman_options[(I)${(q)one_or_more}*]} + ${_shtab_ahriman_options[(I)${(q)remainder}*]} == 0)); then # noqa: E501
_shtab_ahriman_options+=(': :_shtab_ahriman_commands' '*::: :->ahriman')
fi
_arguments -C $_shtab_ahriman_options
_arguments -C -s $_shtab_ahriman_options
case $state in
ahriman)
@ -564,70 +590,72 @@ _shtab_ahriman() {
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:_shtab_ahriman-$line[1]:"
case $line[1] in
add) _arguments -C $_shtab_ahriman_add_options ;;
aur-search) _arguments -C $_shtab_ahriman_aur_search_options ;;
check) _arguments -C $_shtab_ahriman_check_options ;;
clean) _arguments -C $_shtab_ahriman_clean_options ;;
config) _arguments -C $_shtab_ahriman_config_options ;;
config-validate) _arguments -C $_shtab_ahriman_config_validate_options ;;
daemon) _arguments -C $_shtab_ahriman_daemon_options ;;
help) _arguments -C $_shtab_ahriman_help_options ;;
help-commands-unsafe) _arguments -C $_shtab_ahriman_help_commands_unsafe_options ;;
help-updates) _arguments -C $_shtab_ahriman_help_updates_options ;;
help-version) _arguments -C $_shtab_ahriman_help_version_options ;;
init) _arguments -C $_shtab_ahriman_init_options ;;
key-import) _arguments -C $_shtab_ahriman_key_import_options ;;
package-add) _arguments -C $_shtab_ahriman_package_add_options ;;
package-remove) _arguments -C $_shtab_ahriman_package_remove_options ;;
package-status) _arguments -C $_shtab_ahriman_package_status_options ;;
package-status-remove) _arguments -C $_shtab_ahriman_package_status_remove_options ;;
package-status-update) _arguments -C $_shtab_ahriman_package_status_update_options ;;
package-update) _arguments -C $_shtab_ahriman_package_update_options ;;
patch-add) _arguments -C $_shtab_ahriman_patch_add_options ;;
patch-list) _arguments -C $_shtab_ahriman_patch_list_options ;;
patch-remove) _arguments -C $_shtab_ahriman_patch_remove_options ;;
patch-set-add) _arguments -C $_shtab_ahriman_patch_set_add_options ;;
rebuild) _arguments -C $_shtab_ahriman_rebuild_options ;;
remove) _arguments -C $_shtab_ahriman_remove_options ;;
remove-unknown) _arguments -C $_shtab_ahriman_remove_unknown_options ;;
repo-backup) _arguments -C $_shtab_ahriman_repo_backup_options ;;
repo-check) _arguments -C $_shtab_ahriman_repo_check_options ;;
repo-clean) _arguments -C $_shtab_ahriman_repo_clean_options ;;
repo-config) _arguments -C $_shtab_ahriman_repo_config_options ;;
repo-config-validate) _arguments -C $_shtab_ahriman_repo_config_validate_options ;;
repo-daemon) _arguments -C $_shtab_ahriman_repo_daemon_options ;;
repo-init) _arguments -C $_shtab_ahriman_repo_init_options ;;
repo-rebuild) _arguments -C $_shtab_ahriman_repo_rebuild_options ;;
repo-remove-unknown) _arguments -C $_shtab_ahriman_repo_remove_unknown_options ;;
repo-report) _arguments -C $_shtab_ahriman_repo_report_options ;;
repo-restore) _arguments -C $_shtab_ahriman_repo_restore_options ;;
repo-setup) _arguments -C $_shtab_ahriman_repo_setup_options ;;
repo-sign) _arguments -C $_shtab_ahriman_repo_sign_options ;;
repo-status-update) _arguments -C $_shtab_ahriman_repo_status_update_options ;;
repo-sync) _arguments -C $_shtab_ahriman_repo_sync_options ;;
repo-tree) _arguments -C $_shtab_ahriman_repo_tree_options ;;
repo-triggers) _arguments -C $_shtab_ahriman_repo_triggers_options ;;
repo-update) _arguments -C $_shtab_ahriman_repo_update_options ;;
report) _arguments -C $_shtab_ahriman_report_options ;;
search) _arguments -C $_shtab_ahriman_search_options ;;
service-clean) _arguments -C $_shtab_ahriman_service_clean_options ;;
service-config) _arguments -C $_shtab_ahriman_service_config_options ;;
service-config-validate) _arguments -C $_shtab_ahriman_service_config_validate_options ;;
service-key-import) _arguments -C $_shtab_ahriman_service_key_import_options ;;
service-setup) _arguments -C $_shtab_ahriman_service_setup_options ;;
service-shell) _arguments -C $_shtab_ahriman_service_shell_options ;;
setup) _arguments -C $_shtab_ahriman_setup_options ;;
shell) _arguments -C $_shtab_ahriman_shell_options ;;
sign) _arguments -C $_shtab_ahriman_sign_options ;;
status) _arguments -C $_shtab_ahriman_status_options ;;
status-update) _arguments -C $_shtab_ahriman_status_update_options ;;
sync) _arguments -C $_shtab_ahriman_sync_options ;;
update) _arguments -C $_shtab_ahriman_update_options ;;
user-add) _arguments -C $_shtab_ahriman_user_add_options ;;
user-list) _arguments -C $_shtab_ahriman_user_list_options ;;
user-remove) _arguments -C $_shtab_ahriman_user_remove_options ;;
version) _arguments -C $_shtab_ahriman_version_options ;;
web) _arguments -C $_shtab_ahriman_web_options ;;
add) _arguments -C -s $_shtab_ahriman_add_options ;;
aur-search) _arguments -C -s $_shtab_ahriman_aur_search_options ;;
check) _arguments -C -s $_shtab_ahriman_check_options ;;
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 ;;
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 ;;
help-updates) _arguments -C -s $_shtab_ahriman_help_updates_options ;;
help-version) _arguments -C -s $_shtab_ahriman_help_version_options ;;
init) _arguments -C -s $_shtab_ahriman_init_options ;;
key-import) _arguments -C -s $_shtab_ahriman_key_import_options ;;
package-add) _arguments -C -s $_shtab_ahriman_package_add_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 ;;
package-status-update) _arguments -C -s $_shtab_ahriman_package_status_update_options ;;
package-update) _arguments -C -s $_shtab_ahriman_package_update_options ;;
patch-add) _arguments -C -s $_shtab_ahriman_patch_add_options ;;
patch-list) _arguments -C -s $_shtab_ahriman_patch_list_options ;;
patch-remove) _arguments -C -s $_shtab_ahriman_patch_remove_options ;;
patch-set-add) _arguments -C -s $_shtab_ahriman_patch_set_add_options ;;
rebuild) _arguments -C -s $_shtab_ahriman_rebuild_options ;;
remove) _arguments -C -s $_shtab_ahriman_remove_options ;;
remove-unknown) _arguments -C -s $_shtab_ahriman_remove_unknown_options ;;
repo-backup) _arguments -C -s $_shtab_ahriman_repo_backup_options ;;
repo-check) _arguments -C -s $_shtab_ahriman_repo_check_options ;;
repo-clean) _arguments -C -s $_shtab_ahriman_repo_clean_options ;;
repo-config) _arguments -C -s $_shtab_ahriman_repo_config_options ;;
repo-config-validate) _arguments -C -s $_shtab_ahriman_repo_config_validate_options ;;
repo-create-keyring) _arguments -C -s $_shtab_ahriman_repo_create_keyring_options ;;
repo-create-mirrorlist) _arguments -C -s $_shtab_ahriman_repo_create_mirrorlist_options ;;
repo-daemon) _arguments -C -s $_shtab_ahriman_repo_daemon_options ;;
repo-init) _arguments -C -s $_shtab_ahriman_repo_init_options ;;
repo-rebuild) _arguments -C -s $_shtab_ahriman_repo_rebuild_options ;;
repo-remove-unknown) _arguments -C -s $_shtab_ahriman_repo_remove_unknown_options ;;
repo-report) _arguments -C -s $_shtab_ahriman_repo_report_options ;;
repo-restore) _arguments -C -s $_shtab_ahriman_repo_restore_options ;;
repo-setup) _arguments -C -s $_shtab_ahriman_repo_setup_options ;;
repo-sign) _arguments -C -s $_shtab_ahriman_repo_sign_options ;;
repo-status-update) _arguments -C -s $_shtab_ahriman_repo_status_update_options ;;
repo-sync) _arguments -C -s $_shtab_ahriman_repo_sync_options ;;
repo-tree) _arguments -C -s $_shtab_ahriman_repo_tree_options ;;
repo-triggers) _arguments -C -s $_shtab_ahriman_repo_triggers_options ;;
repo-update) _arguments -C -s $_shtab_ahriman_repo_update_options ;;
report) _arguments -C -s $_shtab_ahriman_report_options ;;
search) _arguments -C -s $_shtab_ahriman_search_options ;;
service-clean) _arguments -C -s $_shtab_ahriman_service_clean_options ;;
service-config) _arguments -C -s $_shtab_ahriman_service_config_options ;;
service-config-validate) _arguments -C -s $_shtab_ahriman_service_config_validate_options ;;
service-key-import) _arguments -C -s $_shtab_ahriman_service_key_import_options ;;
service-setup) _arguments -C -s $_shtab_ahriman_service_setup_options ;;
service-shell) _arguments -C -s $_shtab_ahriman_service_shell_options ;;
setup) _arguments -C -s $_shtab_ahriman_setup_options ;;
shell) _arguments -C -s $_shtab_ahriman_shell_options ;;
sign) _arguments -C -s $_shtab_ahriman_sign_options ;;
status) _arguments -C -s $_shtab_ahriman_status_options ;;
status-update) _arguments -C -s $_shtab_ahriman_status_update_options ;;
sync) _arguments -C -s $_shtab_ahriman_sync_options ;;
update) _arguments -C -s $_shtab_ahriman_update_options ;;
user-add) _arguments -C -s $_shtab_ahriman_user_add_options ;;
user-list) _arguments -C -s $_shtab_ahriman_user_list_options ;;
user-remove) _arguments -C -s $_shtab_ahriman_user_remove_options ;;
version) _arguments -C -s $_shtab_ahriman_version_options ;;
web) _arguments -C -s $_shtab_ahriman_web_options ;;
esac
esac
}

View File

@ -14,7 +14,6 @@ import os
import sys
from pathlib import Path
from unittest import mock
from ahriman.version import __version__
@ -24,13 +23,6 @@ sys.path.insert(0, str(basedir))
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
for module in (
"pyalpm",
):
if module in sys.modules:
continue
sys.modules[module] = mock.Mock()
# -- Project information -----------------------------------------------------
@ -92,6 +84,8 @@ autoclass_content = "both"
autodoc_member_order = "groupwise"
autodoc_mock_imports = ["pyalpm"]
autodoc_default_options = {
"no-undoc-members": True,
}

View File

@ -30,8 +30,8 @@ Base configuration settings.
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
* ``suppress_http_log_errors`` - suppress http log errors, boolean, optional, default ``no``. If set to ``yes``, any http log errors (e.g. if web server is not available, but http logging is enabled) will be suppressed.
``alpm`` group
--------------
``alpm:*`` groups
-----------------
libalpm and AUR related configuration. Group name can refer to architecture, e.g. ``alpm:x86_64`` can be used for x86_64 architecture specific settings.
@ -69,6 +69,7 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
* ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional.
* ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional.
* ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of mention.
* ``triggers_known`` - optional list of ``ahriman.core.triggers.Trigger`` class implementations which are not run automatically and used only for trigger discovery and configuration validation.
* ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, int, optional, default ``604800``.
``repository`` group
@ -86,7 +87,6 @@ Settings for signing packages or repository. Group name can refer to architectur
* ``target`` - configuration flag to enable signing, space separated list of strings, required. Allowed values are ``package`` (sign each package separately), ``repository`` (sign repository database file).
* ``key`` - default PGP key, string, required. This key will also be used for database signing if enabled.
* ``key_*`` settings - PGP key which will be used for specific packages, string, optional. For example, if there is ``key_yay`` option the specified key will be used for yay package and default key for others.
``web:*`` groups
----------------
@ -107,6 +107,41 @@ Web server settings. If any of ``host``/``port`` is not set, web integration wil
* ``unix_socket_unsafe`` - set unsafe (o+w) permissions to unix socket, boolean, optional, default ``yes``. This option is enabled by default, because it is supposed that unix socket is created in safe environment (only web service is supposed to be used in unsafe), but it can be disabled by configuration.
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.
``keyring`` group
--------------------
Keyring package generator plugin.
* ``target`` - list of generator settings sections, space separated list of strings, required. It must point to valid section name.
Keyring generator plugin
^^^^^^^^^^^^^^^^^^^^^^^^
* ``description`` - keyring package description, string, optional, default is ``repo PGP keyring``, where ``repo`` is the repository name.
* ``homepage`` - url to homepage location if any, string, optional.
* ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``.
* ``package`` - keyring package name, string, optional, default is ``repo-keyring``, where ``repo`` is the repository name.
* ``packagers`` - list of packagers keys, space separated list of strings, optional, if not set, the ``key_*`` options from ``sign`` group will be used.
* ``revoked`` - list of revoked packagers keys, space separated list of strings, optional.
* ``trusted`` - list of master keys, space separated list of strings, optional, if not set, the ``key`` option from ``sign`` group will be used.
``mirrorlist`` group
--------------------
Mirrorlist package generator plugin.
* ``target`` - list of generator settings sections, space separated list of strings, required. It must point to valid section name.
Mirrorlist generator plugin
^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ``description`` - mirrorlist package description, string, optional, default is ``repo mirror list for use by pacman``, where ``repo`` is the repository name.
* ``homepage`` - url to homepage location if any, string, optional.
* ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``.
* ``package`` - mirrorlist package name, string, optional, default is ``repo-mirrorlist``, where ``repo`` is the repository name.
* ``path`` - absolute path to generated mirrorlist file, string, optional, default is ``/etc/pacman.d/repo-mirrorlist``, where ``repo`` is the repository name.
* ``servers`` - list of repository mirrors, space separated list of strings, required.
``remote-pull`` group
---------------------

View File

@ -1,4 +1,3 @@
FAQ
===
@ -197,7 +196,7 @@ Alternatively you can create full-diff patches, which are calculated by using ``
.. code-block:: shell
sudo -u ahriman ahriman patch-set-add /path/to/local/directory/with/PKGBUILD
sudo -u ahriman ahriman patch-set-add /path/to/local/directory/with/PKGBUILD
The last command will calculate diff from current tree to the ``HEAD`` and will store it locally. Patches will be applied on any package actions (e.g. it can be used for dependency management).
@ -392,7 +391,7 @@ The following environment variables are supported:
* ``AHRIMAN_FORCE_ROOT`` - force run ahriman as root instead of guessing by subcommand.
* ``AHRIMAN_HOST`` - host for the web interface, default is ``0.0.0.0``.
* ``AHRIMAN_MULTILIB`` - if set (default) multilib repository will be used, disabled otherwise.
* ``AHRIMAN_OUTPUT`` - controls logging handler, e.g. ``syslog``, ``console``. The name must be found in logging configuration. Note that if ``syslog`` (the default) handler is used you will need to mount ``/dev/log`` inside container because it is not available there.
* ``AHRIMAN_OUTPUT`` - controls logging handler, e.g. ``syslog``, ``console``. The name must be found in logging configuration. Note that if ``syslog`` handler is used you will need to mount ``/dev/log`` inside container because it is not available there.
* ``AHRIMAN_PACKAGER`` - packager name from which packages will be built, default is ``ahriman bot <ahriman@example.com>``.
* ``AHRIMAN_PACMAN_MIRROR`` - override pacman mirror server if set.
* ``AHRIMAN_PORT`` - HTTP server port if any, default is empty.
@ -664,7 +663,7 @@ How to report by email
.. code-block:: shell
yay -S python-jinja
yay -S --asdeps python-jinja
#.
Configure the service:
@ -691,7 +690,7 @@ How to generate index page for S3
.. code-block:: shell
yay -S python-jinja
yay -S --asdeps python-jinja
#.
Configure the service:
@ -715,7 +714,7 @@ How to post build report to telegram
.. code-block:: shell
yay -S python-jinja
yay -S --asdeps python-jinja
#.
Register bot in telegram. You can do it by talking with `@BotFather <https://t.me/botfather>`_. For more details please refer to `official documentation <https://core.telegram.org/bots>`_.
@ -754,6 +753,80 @@ If you did everything fine you should receive the message with the next update.
(replace ``${CHAT_ID}`` and ``${API_KEY}`` with the values from configuration).
Maintenance packages
--------------------
Generate keyring package
^^^^^^^^^^^^^^^^^^^^^^^^
The application provides special plugin which generates keyring package. This plugin heavily depends on ``sign`` group settings, however it is possible to override them. The minimal package can be generated in the following way:
#.
Edit configuration:
.. code-block:: ini
[keyring]
target = keyring_generator
By default it will use ``sign.key`` as trusted key and all other keys as packagers ones. For all available options refer to :doc:`configuration <configuration>`.
#.
Create package source files:
.. code-block:: shell
sudo -u ahriman ahriman repo-create-keyring
This command will generate PKGBUILD, revoked and trusted listings and keyring itself and will register the package in database.
#.
Build new package as usual:
.. code-block:: shell
sudo -u ahriman ahriman package-add aur-clone-keyring --source local --now
where ``aur-clone`` is your repository name.
This plugin might have some issues, in case of any of them, kindly create `new issue <https://github.com/arcan1s/ahriman/issues/new/choose>`_.
Generate mirrorlist package
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The application provides special plugin which generates mirrorlist package also. It is possible to distribute this package as usual later. The package can be generated in the following way:
#.
Edit configuration:
.. code-block:: ini
[mirrorlist]
target = mirrorlist_generator
[mirrorlist_generator]
servers = https://repo.example.com/$arch
The ``mirrorlist_generator.servers`` must contain list of available mirrors, the ``$arch`` and ``$repo`` variables are supported. For more options kindly refer to :doc:`configuration <configuration>`.
#.
Create package source files:
.. code-block:: shell
sudo -u ahriman ahriman repo-create-mirrorlist
This command will generate PKGBUILD and mirrorlist file and will register the package in database.
#.
Build new package as usual:
.. code-block:: shell
sudo -u ahriman ahriman package-add aur-clone-mirrorlist --source local --now
where ``aur-clone`` is your repository name.
Web service
-----------
@ -765,7 +838,7 @@ How to setup web service
.. code-block:: shell
yay -S python-aiohttp python-aiohttp-jinja2
yay -S --asdeps python-aiohttp python-aiohttp-jinja2 python-aiohttp-apispec>=3.0.0 python-aiohttp-cors
#.
Configure service:
@ -786,15 +859,16 @@ How to enable basic authorization
.. code-block:: shell
yay -S python-aiohttp-security python-aiohttp-session python-cryptography
yay -S --asdeps python-aiohttp-security python-aiohttp-session python-cryptography
#.
Configure the service to enable authorization:
Configure the service to enable authorization (``salt`` can be generated as any random string):
.. code-block:: ini
[auth]
target = configuration
salt = somerandomstring
#.
In order to provide access for reporting from application instances you can (recommended way) use unix sockets by configuring the following (note, that it requires ``python-requests-unixsocket`` package to be installed):
@ -842,7 +916,7 @@ How to enable OAuth authorization
.. code-block:: shell
yay -S python-aiohttp-security python-aiohttp-session python-cryptography python-aioauth-client
yay -S --asdeps python-aiohttp-security python-aiohttp-session python-cryptography python-aioauth-client
#.
Configure the service:
@ -860,7 +934,7 @@ How to enable OAuth authorization
Configure ``oauth_provider`` and ``oauth_scopes`` in case if you would like to use different from Google provider. Scope must grant access to user email. ``web.address`` is required to make callback URL available from internet.
#.
Create service user:
If you are not going to use unix socket, you also need to create service user (remember to set ``auth.salt`` option before):
.. code-block:: shell
@ -878,6 +952,11 @@ How to enable OAuth authorization
#.
Restart web service ``systemctl restart ahriman-web@x86_64``.
How to implement own interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can write your own interface by using API which is provided by the web service. Full autogenerated API documentation is available at ``http://localhost:8080/api-docs``.
Backup and restore
------------------
@ -949,6 +1028,11 @@ Don't know, haven't tried it. But it lacks of documentation at least.
* ``repoctl`` is able to store old packages.
* Ability to host repository from same command in ``repoctl`` vs external services (e.g. nginx) in ``ahriman``.
`repod <https://gitlab.archlinux.org/archlinux/repod>`_
"""""""""""""""""""""""""""""""""""""""""""""""""""""""
Official tool provided by distribution, has clean logic, but it is just a helper for ``repo-add``, e.g. it doesn't work with AUR and all packages builds have to be handled separately.
`repo-scripts <https://github.com/arcan1s/repo-scripts>`_
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@ -970,13 +1054,17 @@ It is automation tools for ``repoctl`` mentioned above. Except for using shell i
How to check service logs
^^^^^^^^^^^^^^^^^^^^^^^^^
By default, the service writes logs to ``/dev/log`` which can be accessed by using ``journalctl`` command (logs are written to the journal of the user under which command is run).
You can also edit configuration and forward logs to ``stderr``, just change ``handlers`` value, e.g.:
By default, the service writes logs to ``journald`` which can be accessed by using ``journalctl`` command (logs are written to the journal of the user under which command is run). In order to retrieve logs for the process you can use the following command:
.. code-block:: shell
sed -i 's/handlers = syslog_handler/handlers = console_handler/g' /etc/ahriman.ini.d/logging.ini
sudo journalctl SYSLOG_IDENTIFIER=ahriman
You can also ask to forward logs to ``stderr``, just set ``--log-handler`` flag, e.g.:
.. code-block:: shell
ahriman --log-handler console ...
You can even configure logging as you wish, but kindly refer to python ``logging`` module `configuration <https://docs.python.org/3/library/logging.config.html>`_. The application uses java concept to log messages, e.g. class ``Application`` imported from ``ahriman.application.application`` package will have logger called ``ahriman.application.application.Application``. In order to e.g. change logger name for whole application package it is possible to change values for ``ahriman.application`` package; thus editing ``ahriman`` logger configuration will change logging for whole application (unless there are overrides for another logger).

View File

@ -12,14 +12,14 @@ Features
* VCS packages support.
* Official repository support.
* Ability to patch AUR packages and even create package from local PKGBUILDs.
* Sign support with gpg (repository, package, per package settings).
* Sign support with gpg (repository, package), multiple packagers support.
* Triggers for repository updates, e.g. synchronization to remote services (rsync, s3 and github) and report generation (email, html, telegram).
* Repository status interface with optional authorization and control options.
Live demos
----------
* `Build status page <https://ahriman-demo.arcanis.me>`_. You can login as ``demo`` user by using ``demo`` password. Note, however, you will not be able to run tasks.
* `Build status page <https://ahriman-demo.arcanis.me>`_. You can login as ``demo`` user by using ``demo`` password. Note, however, you will not be able to run tasks. `HTTP API documentation <https://ahriman-demo.arcanis.me/api-docs>`_ is also available.
* `Repository index <http://repo.arcanis.me/x86_64/index.html>`_.
* `Telegram feed <https://t.me/arcanisrepo>`_.

View File

@ -32,11 +32,11 @@ Initial setup
ln -s /usr/bin/archbuild /usr/local/bin/ahriman-x86_64-build
#.
Create configuration file (same as previous ``pacman-{name}.conf``):
Create configuration file (same as previous ``{name}.conf``):
.. code-block:: shell
cp /usr/share/devtools/pacman-{extra,ahriman}.conf
cp /usr/share/devtools/pacman.conf.d/{extra,ahriman}.conf
#.
Change configuration file, add your own repository, add multilib repository etc:
@ -44,11 +44,11 @@ Initial setup
.. code-block:: shell
echo '[multilib]' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'Include = /etc/pacman.d/mirrorlist' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'Include = /etc/pacman.d/mirrorlist' | tee -a /usr/share/devtools/pacman.conf.d/ahriman.conf
echo '[aur-clone]' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'SigLevel = Optional TrustAll' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'Server = file:///var/lib/ahriman/repository/$arch' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'SigLevel = Optional TrustAll' | tee -a /usr/share/devtools/pacman.conf.d/ahriman.conf
echo 'Server = file:///var/lib/ahriman/repository/$arch' | tee -a /usr/share/devtools/pacman.conf.d/ahriman.conf
#.
Set ``build_command`` option to point to your command:
@ -64,7 +64,7 @@ Initial setup
.. code-block:: shell
echo 'Cmnd_Alias CARCHBUILD_CMD = /usr/local/bin/ahriman-x86_64-build *' | tee -a /etc/sudoers.d/ahriman
echo 'ahriman ALL=(ALL) NOPASSWD: CARCHBUILD_CMD' | tee -a /etc/sudoers.d/ahriman
echo 'ahriman ALL=(ALL) NOPASSWD:SETENV: CARCHBUILD_CMD' | tee -a /etc/sudoers.d/ahriman
chmod 400 /etc/sudoers.d/ahriman
This command supports several arguments, kindly refer to its help message.

View File

@ -31,6 +31,16 @@ This trigger will be called right after build process (``on_result``). It will p
Trigger which can be used for reporting. It implements ``on_result`` method and thus being called on each build update and generates report (e.g. html, telegram etc) according to the current settings.
``ahriman.core.support.KeyringTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Generator for keyring package. This trigger will extract keys from local keychain and pack them into keyring specific format. This trigger will generate sources including PKGBUILD, which can be used later for package building.
``ahriman.core.support.MirrorlistTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Simple generator for mirrorlist package, based on the urls which were set by configuration. This trigger will generate sources including PKGBUILD, which can be used later for package building.
``ahriman.core.upload.UploadTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -73,7 +83,7 @@ The ``context`` also implements collection methods such as ``__iter__`` and ``__
Trigger example
---------------
Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`_, which provides easy HTTP API for integration triggers).gre
Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`_, which provides easy HTTP API for integration triggers).
In order to post message to slack we will need a specific trigger url (something like ``https://hooks.slack.com/services/company_id/trigger_id``), channel (e.g. ``#archrepo``) and username (``repo-bot``).

BIN
github-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,19 +1,21 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=2.7.1
pkgver=2.10.2
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')
url="https://github.com/arcan1s/ahriman"
license=('GPL3')
depends=('devtools' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-requests' 'python-setuptools' 'python-srcinfo')
depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
makedepends=('python-build' 'python-installer' 'python-wheel')
optdepends=('breezy: -bzr packages support'
'darcs: -darcs packages support'
'mercurial: -hg packages support'
'python-aioauth-client: web server with OAuth2 authorization'
'python-aiohttp: web server'
'python-aiohttp-apispec>=3.0.0: web server'
'python-aiohttp-cors: web server'
'python-aiohttp-debugtoolbar: web server with enabled debug panel'
'python-aiohttp-jinja2: web server'
'python-aiohttp-security: web server with authorization'
@ -22,11 +24,13 @@ optdepends=('breezy: -bzr packages support'
'python-cryptography: web server with authorization'
'python-requests-unixsocket: client report to web server by unix socket'
'python-jinja: html report generation'
'python-systemd: journal support'
'rsync: sync by using rsync'
'subversion: -svn packages support')
source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgname-$pkgver-src.tar.xz"
'ahriman.sysusers'
'ahriman.tmpfiles')
install="$pkgname.install"
backup=('etc/ahriman.ini'
'etc/ahriman.ini.d/logging.ini')
@ -50,6 +54,6 @@ package() {
install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf"
}
sha512sums=('112b0d8aac68e5330bbdd2b86a59c8a9af8ab7a7c636489623c8460bb90f1318585851edd2a97a8ce20e2d2ad93b847b522685df707c190aa39d23ab908fa8ef'
sha512sums=('19841842641520b573cdde6cb80a7cfcd69756d323fdfeebc2eee2d264a1325ead4ab2f8383bb369f7896bfc1de59d7358f133f4afeb90a9b9f0695f482a58d0'
'53d37efec812afebf86281716259f9ea78a307b83897166c72777251c3eebcb587ecee375d907514781fb2a5c808cbb24ef9f3f244f12740155d0603bf213131'
'62b2eccc352d33853ef243c9cddd63663014aa97b87242f1b5bc5099a7dbd69ff3821f24ffc58e1b7f2387bd4e9e9712cc4c67f661b1724ad99cdf09b3717794')

View File

@ -0,0 +1,25 @@
post_upgrade() {
local breakpoints=(
2.9.0-1
)
for v in "${breakpoints[@]}"; do
if [[ $(vercmp "$v" "$2") -eq 1 ]]; then
"_${v//[.-]/_}_changes"
fi
done
}
_2_9_0_1_changes() {
cat << EOF
It was found that you were upgrading from old-devtools package to the new one, which requires manual intervention:
* make sure that devtools are upgraded to the latest release;
* merge service configuration if required;
* run setup command (i.e. sudo ahriman service-setup) with the same arguments as you did before;
* remove build chroot: sudo rm -r /var/lib/ahriman/chroot/ahriman-x86_64/;
* update local databases: sudo -u ahriman ahriman update --no-aur --no-local --no-manual -yy;
For more information kindly refer to changelog https://github.com/arcan1s/ahriman/releases/tag/2.9.0
EOF
}

View File

@ -1 +1 @@
u ahriman 643 "ArcH linux ReposItory MANager" /var/lib/ahriman
u ahriman 643 "ArcH linux ReposItory MANager" /var/lib/ahriman

View File

@ -1,2 +1 @@
d /var/lib/ahriman 0755 ahriman ahriman
d /var/log/ahriman 0755 ahriman ahriman
d /var/lib/ahriman 0755 ahriman ahriman

View File

@ -7,7 +7,7 @@ suppress_http_log_errors = yes
[alpm]
database = /var/lib/pacman
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
repositories = core extra community multilib
repositories = core extra multilib
root = /
use_ahriman_cache = yes
@ -25,6 +25,7 @@ ignore_packages =
makechrootpkg_flags =
makepkg_flags = --nocolor --ignorearch
triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.gitremote.RemotePushTrigger
triggers_known = ahriman.core.gitremote.RemotePullTrigger ahriman.core.gitremote.RemotePushTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.support.KeyringTrigger ahriman.core.support.MirrorlistTrigger
vcs_allowed_age = 604800
[repository]
@ -34,6 +35,12 @@ root = /var/lib/ahriman
[sign]
target =
[keyring]
target =
[mirrorlist]
target =
[remote-pull]
target =

View File

@ -2,17 +2,23 @@
keys = root,http,stderr,boto3,botocore,nose,s3transfer
[handlers]
keys = console_handler,syslog_handler
keys = console_handler,journald_handler,syslog_handler
[formatters]
keys = generic_format,syslog_format
[handler_console_handler]
class = StreamHandler
class = logging.StreamHandler
level = DEBUG
formatter = generic_format
args = (sys.stderr,)
[handler_journald_handler]
class = ahriman.core.log.journal_handler.JournalHandler
level = DEBUG
formatter = syslog_format
kwargs = {"SYSLOG_IDENTIFIER": "ahriman"}
[handler_syslog_handler]
class = logging.handlers.SysLogHandler
level = DEBUG
@ -21,20 +27,16 @@ args = ("/dev/log",)
[formatter_generic_format]
format = [%(levelname)s %(asctime)s] [%(name)s]: %(message)s
datefmt =
[formatter_syslog_format]
format = [%(levelname)s] [%(name)s]: %(message)s
datefmt =
[logger_root]
level = DEBUG
handlers = syslog_handler
qualname = root
[logger_http]
level = DEBUG
handlers = syslog_handler
qualname = http
propagate = 0
@ -45,24 +47,20 @@ qualname = stderr
[logger_boto3]
level = INFO
handlers = syslog_handler
qualname = boto3
propagate = 0
[logger_botocore]
level = INFO
handlers = syslog_handler
qualname = botocore
propagate = 0
[logger_nose]
level = INFO
handlers = syslog_handler
qualname = nose
propagate = 0
[logger_s3transfer]
level = INFO
handlers = syslog_handler
qualname = s3transfer
propagate = 0

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ahriman API</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Embed elements Elements via Web Component -->
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css" type="text/css">
<link rel="shortcut icon" href="/static/favicon.ico">
</head>
<body>
<elements-api
apiDescriptionUrl="/api-docs/swagger.json"
router="hash"
layout="sidebar"
/>
</body>
</html>

View File

@ -63,6 +63,8 @@
<table id="packages" class="table table-striped table-hover"
data-export-options='{"fileName": "packages"}'
data-filter-control="true"
data-filter-control-visible="false"
data-page-list="[10, 25, 50, 100, all]"
data-page-size="10"
data-pagination="true"
@ -72,6 +74,7 @@
data-show-columns-search="true"
data-show-columns-toggle-all="true"
data-show-export="true"
data-show-filter-control-switch="true"
data-show-fullscreen="true"
data-show-search-clear-button="true"
data-sortable="true"
@ -82,13 +85,14 @@
<thead class="table-primary">
<tr>
<th data-checkbox="true"></th>
<th data-sortable="true" data-switchable="false" data-field="base">package base</th>
<th data-sortable="true" data-field="version">version</th>
<th data-sortable="true" data-field="packages">packages</th>
<th data-sortable="true" data-visible="false" data-field="groups">groups</th>
<th data-sortable="true" data-visible="false" data-field="licenses">licenses</th>
<th data-sortable="true" data-field="timestamp">last update</th>
<th data-sortable="true" data-cell-style="statusFormat" data-field="status">status</th>
<th data-sortable="true" data-switchable="false" data-field="base" data-filter-control="input" data-filter-control-placeholder="(any base)">package base</th>
<th data-sortable="true" data-field="version" data-filter-control="input" data-filter-control-placeholder="(any version)">version</th>
<th data-sortable="true" data-field="packages" data-filter-control="input" data-filter-control-placeholder="(any package)">packages</th>
<th data-sortable="true" data-visible="false" data-field="groups" data-filter-control="select" data-filter-data="func:filterListGroups" data-filter-custom-search="filterList" data-filter-control-placeholder="(any group)">groups</th>
<th data-sortable="true" data-visible="false" data-field="licenses" data-filter-control="select" data-filter-data="func:filterListLicenses" data-filter-custom-search="filterList" data-filter-control-placeholder="(any license)">licenses</th>
<th data-sortable="true" data-visible="false" data-field="packager" data-filter-control="select" data-filter-custom-search="filterContains" data-filter-control-placeholder="(any packager)">packager</th>
<th data-sortable="true" data-field="timestamp" data-filter-control="input" data-filter-custom-search="filterDateRange" data-filter-control-placeholder="(any date)">last update</th>
<th data-sortable="true" data-cell-style="statusFormat" data-field="status" data-filter-control="select" data-filter-control-placeholder="(any status)">status</th>
</tr>
</thead>
</table>
@ -100,6 +104,7 @@
<li><a id="badge-version" class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
<li><a class="nav-link" href="/api-docs" title="API documentation">api</a></li>
</ul>
{% if index_url is not none %}

View File

@ -84,7 +84,7 @@
showSuccess("Success", `Key ${key} has been imported`);
},
error: (jqXHR, _, errorThrown) => {
const message = _ => { return `Could not import key ${key} from ${server}`; };
const message = _ => `Could not import key ${key} from ${server}`;
showFailure("Action failed", message, jqXHR, errorThrown);
},
});

View File

@ -60,8 +60,8 @@
const packages = packageInput.val();
if (packages) {
packageAddModal.modal("hide");
const onSuccess = update => { return `Packages ${update} have been added`; };
const onFailure = error => { return `Package addition failed: ${error}`; };
const onSuccess = update => `Packages ${update} have been added`;
const onFailure = error => `Package addition failed: ${error}`;
doPackageAction("/api/v1/service/add", [packages], onSuccess, onFailure);
}
}
@ -70,8 +70,8 @@
const packages = packageInput.val();
if (packages) {
packageAddModal.modal("hide");
const onSuccess = update => { return `Packages ${update} have been requested`; };
const onFailure = error => { return `Package request failed: ${error}`; };
const onSuccess = update => `Packages ${update} have been requested`;
const onFailure = error => `Package request failed: ${error}`;
doPackageAction("/api/v1/service/request", [packages], onSuccess, onFailure);
}
}

View File

@ -61,7 +61,7 @@
error: (jqXHR, _, errorThrown) => {
// show failed modal in case if first time loading
if (isPackageBaseSet) {
const message = error => { return `Could not load package ${packageBase} logs: ${error}`; };
const message = error => `Could not load package ${packageBase} logs: ${error}`;
showFailure("Load failure", message, jqXHR, errorThrown);
}
},

View File

@ -33,8 +33,8 @@
const packages = dependencyInput.val();
if (packages) {
packageRebuildModal.modal("hide");
const onSuccess = update => { return `Repository rebuild has been run for packages which depend on ${update}`; };
const onFailure = error => { return `Repository rebuild failed: ${error}`; };
const onSuccess = update => `Repository rebuild has been run for packages which depend on ${update}`;
const onFailure = error => `Repository rebuild failed: ${error}`;
doPackageAction("/api/v1/service/rebuild", [packages], onSuccess, onFailure);
}
}

View File

@ -15,6 +15,25 @@
table.bootstrapTable(method, {field: "id", values: [data.id]});
} else showLogs(data.id);
});
table.on("created-controls.bs.table", () => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
pickerInput.on("apply.daterangepicker", (event, picker) => {
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
table.bootstrapTable("triggerSearch");
});
pickerInput.on("cancel.daterangepicker", () => {
pickerInput.val("");
table.bootstrapTable("triggerSearch");
});
});
const repositoryBadge = $("#badge-repository");
const statusBadge = $("#badge-status");
@ -37,21 +56,21 @@
}
function getSelection() {
return table.bootstrapTable("getSelections").map(row => { return row.id; });
return table.bootstrapTable("getSelections").map(row => row.id);
}
function removePackages() {
const onSuccess = update => { return `Packages ${update} have been removed`; };
const onFailure = error => { return `Could not remove packages: ${error}`; };
const onSuccess = update => `Packages ${update} have been removed`;
const onFailure = error => `Could not remove packages: ${error}`;
doPackageAction("/api/v1/service/remove", getSelection(), onSuccess, onFailure);
}
function updatePackages() {
const currentSelection = getSelection();
const [url, onSuccess] = currentSelection.length === 0
? ["/api/v1/service/update", _ => { return "Repository update has been run"; }]
: ["/api/v1/service/add", update => { return `Run update for packages ${update}`; }];
const onFailure = error => { return `Packages update failed: ${error}`; };
? ["/api/v1/service/update", _ => "Repository update has been run"]
: ["/api/v1/service/add", update => `Run update for packages ${update}`];
const onFailure = error => `Packages update failed: ${error}`;
doPackageAction(url, currentSelection, onSuccess, onFailure);
}
@ -81,13 +100,13 @@
success: response => {
const extractListProperties = (description, property) => {
return Object.values(description.packages)
.map(pkg => { return pkg[property]; })
.reduce((left, right) => { return left.concat(right); }, []);
.map(pkg => pkg[property])
.reduce((left, right) => left.concat(right), []);
};
const listToTable = data => {
return Array.from(new Set(data))
.sort()
.map(entry => { return safe(entry); })
.map(entry => safe(entry))
.join("<br>");
};
@ -98,6 +117,7 @@
id: package_base,
base: web_url ? `<a href="${safe(web_url)}" title="${safe(package_base)}">${safe(package_base)}</a>` : safe(package_base),
version: safe(description.package.version),
packager: description.package.packager ? safe(description.package.packager) : "",
packages: listToTable(Object.keys(description.package.packages)),
groups: listToTable(extractListProperties(description.package, "groups")),
licenses: listToTable(extractListProperties(description.package, "licenses")),
@ -120,8 +140,8 @@
table.bootstrapTable("hideLoading");
} else {
// other errors
const messaga = error => { return `Could not load list of packages: ${error}`; };
showFailure("Load failure", messaga, jqXHR, errorThrown);
const message = error => `Could not load list of packages: ${error}`;
showFailure("Load failure", message, jqXHR, errorThrown);
}
hideControls(true);
},
@ -157,6 +177,18 @@
return {classes: cellClass(value)};
}
function filterListGroups() {
return extractDataList(table.bootstrapTable("getData"), "groups");
}
function filterListLicenses() {
return extractDataList(table.bootstrapTable("getData"), "licenses");
}
function filterListPackagers() {
return extractDataList(table.bootstrapTable("getData"), "packager");
}
$(() => {
table.bootstrapTable({});
statusBadge.popover();

View File

@ -31,6 +31,8 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<div class="container">
<table id="packages" class="table table-striped table-hover"
data-export-options='{"fileName": "packages"}'
data-filter-control="true"
data-filter-control-visible="false"
data-page-list="[10, 25, 50, 100, all]"
data-page-size="10"
data-pagination="true"
@ -40,6 +42,7 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
data-show-columns-search="true"
data-show-columns-toggle-all="true"
data-show-export="true"
data-show-filter-control-switch="true"
data-show-fullscreen="true"
data-show-search-clear-button="true"
data-sortable="true"
@ -48,17 +51,17 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
data-toggle="table">
<thead class="table-primary">
<tr>
<th data-sortable="true" data-switchable="false">package</th>
<th data-sortable="true">version</th>
<th data-sortable="true" data-visible="false">architecture</th>
<th data-sortable="true" data-visible="false">description</th>
<th data-sortable="true" data-visible="false">upstream url</th>
<th data-sortable="true" data-visible="false">licenses</th>
<th data-sortable="true" data-visible="false">groups</th>
<th data-sortable="true" data-visible="false">depends</th>
<th data-sortable="true">archive size</th>
<th data-sortable="true">installed size</th>
<th data-sortable="true">build date</th>
<th data-sortable="true" data-switchable="false" data-field="name" data-filter-control="input" data-filter-control-placeholder="(any package)">package</th>
<th data-sortable="true" data-field="version" data-filter-control="input" data-filter-control-placeholder="(any version)">version</th>
<th data-sortable="true" data-visible="false" data-field="architecture" data-filter-control="select" data-filter-control-placeholder="(any arch)">architecture</th>
<th data-sortable="true" data-visible="false" data-field="description" data-filter-control="input" data-filter-control-placeholder="(any description)">description</th>
<th data-sortable="true" data-visible="false" data-field="url">upstream url</th>
<th data-sortable="true" data-visible="false" data-field="licenses" data-filter-control="select" data-filter-data="func:filterListLicenses" data-filter-custom-search="filterList" data-filter-control-placeholder="(any license)">licenses</th>
<th data-sortable="true" data-visible="false" data-field="groups" data-filter-control="select" data-filter-data="func:filterListGroups" data-filter-custom-search="filterList" data-filter-control-placeholder="(any group)">groups</th>
<th data-sortable="true" data-visible="false" data-field="depends" data-filter-control="select" data-filter-data="func:filterListDepends" data-filter-custom-search="filterList" data-filter-control-placeholder="(any depends)">depends</th>
<th data-sortable="true" data-field="archive_size">archive size</th>
<th data-sortable="true" data-field="installed_size">installed size</th>
<th data-sortable="true" data-field="timestamp" data-filter-control="input" data-filter-custom-search="filterDateRange" data-filter-control-placeholder="(any date)">build date</th>
</tr>
</thead>
@ -96,6 +99,27 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
</div>
<script>
const table = $("#packages");
table.on("created-controls.bs.table", () => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
pickerInput.on("apply.daterangepicker", (event, picker) => {
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
table.bootstrapTable("triggerSearch");
});
pickerInput.on("cancel.daterangepicker", () => {
pickerInput.val("");
table.bootstrapTable("triggerSearch");
});
});
const pacmanConf = $("#pacman-conf");
const pacmanConfCopyButton = $("#copy-btn");
@ -103,6 +127,18 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
const conf = pacmanConf.text();
await copyToClipboard(conf, pacmanConfCopyButton);
}
function filterListDepends() {
return extractDataList(table.bootstrapTable("getData"), "depends");
}
function filterListGroups() {
return extractDataList(table.bootstrapTable("getData"), "groups");
}
function filterListLicenses() {
return extractDataList(table.bootstrapTable("getData"), "licenses");
}
</script>
</body>

View File

@ -1,16 +1,21 @@
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js" integrity="sha384-NXgwF8Kv9SSAr+jemKKcbvQsz+teULH/a5UNJvZc6kP47hZgl62M1vGnw6gHQhb1" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" integrity="sha384-8hHkOkbWN1TLWwet/jpbJ0zbx3FJDeYJgQ8dX1mRrv/vfCfHCqFSFZYCgaMML3z9" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" integrity="sha384-u4eJN1VWrTf/FnYYQJo2kqJyVxEQf5UmWY4iUcNAoLenOEtEuCkfwc5bKvZOWBi5" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.28.0/tableExport.min.js" integrity="sha384-1Rz4Kz/y1rSWw+ZsjTcxB684XgofbO8iizY+UFIzCwFeQ+QUyhBNWBMh/STOyomI" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js" integrity="sha384-IDwe1+LCz02ROU9k972gdyvl+AESN10+x7tBKgc9I5HFtuNz0wWnPclzo6p9vxnk" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-table@1.21.1/dist/bootstrap-table.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" integrity="sha384-IazMVNyYoUNx6357fWJoqtHYUWWCNHIXxFVtbpVgvImQNWuRP2WbHPaIb3QF8j97" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://unpkg.com/bootstrap-table@1.21.1/dist/extensions/export/bootstrap-table-export.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js" integrity="sha384-cuYeSxntonz0PPNlHhBs68uyIAVpIIOZZ5JqeqvYYIcEL727kskC66kF92t6Xl2V" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/bootstrap-table.min.js" integrity="sha384-GVLHfbEvuGA/RFiQ3MK2ClEJkWYJXABg55t9LpoDPZFGIsSq8xhFlQydm5poV2jW" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://unpkg.com/bootstrap-table@1.21.1/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/extensions/export/bootstrap-table-export.min.js" integrity="sha384-jeldDadm+qM2RwGER3qVqxFgWVpAEJ7Jie+0rlYj8ni3KkQA654T8TSXDtol022X" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/extensions/resizable/bootstrap-table-resizable.js" integrity="sha384-wd8Vc6Febikdnsnk9vthRWRvMwffw246vhqiqNO3aSNe1maTEA07Vh3zAQiSyDji" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/extensions/filter-control/bootstrap-table-filter-control.js" integrity="sha384-B6xNXlSOaOFxjlKo9OW3htbox+9/DcaEcjPPEi1+pTMwH5Tzc/s2wNTYriHz7Tb8" crossorigin="anonymous" type="application/javascript"></script>
<script>
async function copyToClipboard(text, button) {
@ -38,4 +43,28 @@
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function extractDataList(data, column) {
const elements = data.flatMap(row => row[column].split("<br>")).filter(v => v); // remove empty elements from array
return Array.from(new Set(elements)).sort();
}
function filterContains(text, value) {
return value.includes(text.toLowerCase().trim());
}
function filterDateRange(text, value) {
const asOfStartOfDay = date => date.setUTCHours(0, 0, 0, 0);
const [minDate, maxDate] = text.split(" - ");
const buildDate = asOfStartOfDay(new Date(value));
return (buildDate >= asOfStartOfDay(new Date(minDate))) && (buildDate <= asOfStartOfDay(new Date(maxDate)));
}
function filterList(index, value, field, data) {
const dataList = extractDataList(data, field);
// the library removes all symbols from string, so it is just string
return value.includes(dataList[index].toLowerCase());
}
</script>

View File

@ -1,11 +1,15 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css" integrity="sha384-LrVLJJYk9OiJmjNDakUBU7kS9qCT8wk1j2OU7ncpsfB3QS37UPdkCuq3ZD1MugNY" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.21.1/dist/bootstrap-table.min.css" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/bootstrap-table.min.css" integrity="sha384-pTEAhytv7JmEG2D7qiW5gY0lI5EKZ9n3CNmj6Qp+U3qhnmH2qnnN9KJbVwbtMHN0" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" integrity="sha384-1sLxvR8mXzjhvFY9f8mzXl97DNLepeZ0PnRiMMdm/rQsKjsrPZPJxYle2wwT2PMg" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.2.2/dist/cosmo/bootstrap.min.css" integrity="sha256-5t++JZpgVLzo9vF7snO5Qw0y3fA5/NkoJENWB7kpg0E=" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/extensions/filter-control/bootstrap-table-filter-control.css" integrity="sha384-4Glx18jZ0Un+yDG6KUpYJ/af8hkssJ02jRASuFv23gfCl0mTXaVMPI9cB4cn3GvE" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.2.3/dist/cosmo/bootstrap.min.css" integrity="sha384-P1PBFVifKf1Ww0gS5B8A0siIeDpcFd4uU7S68LA1XMdE0R+y1WN3DR4HcLc9csRC" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css" integrity="sha384-zLkQsiLfAQqGeIJeKLC+rcCR1YoYaQFLCL7cLDUoKE1ajKJzySpjzWGfYS2vjSG+" crossorigin="anonymous" type="text/css">
<style>
.pre-scrollable {

View File

@ -1,10 +1,10 @@
from pathlib import Path
from setuptools import find_packages, setup
from typing import Any, Dict
from typing import Any
metadata_path = Path(__file__).resolve().parent / "src/ahriman/version.py"
metadata: Dict[str, Any] = {}
metadata: dict[str, Any] = {}
with metadata_path.open() as metadata_file:
exec(metadata_file.read(), metadata) # pylint: disable=exec-used
@ -25,6 +25,7 @@ setup(
packages=find_packages("src"),
package_dir={"": "src"},
package_data={"": ["py.typed"]},
dependency_links=[
],
@ -33,7 +34,6 @@ setup(
"inflection",
"passlib",
"requests",
"setuptools",
"srcinfo",
],
setup_requires=[
@ -68,6 +68,7 @@ setup(
]),
# templates
("share/ahriman/templates", [
"package/share/ahriman/templates/api.jinja2",
"package/share/ahriman/templates/build-status.jinja2",
"package/share/ahriman/templates/email-index.jinja2",
"package/share/ahriman/templates/error.jinja2",
@ -118,7 +119,9 @@ setup(
"shtab",
"sphinx-argparse",
"sphinx-rtd-theme>=1.1.1", # https://stackoverflow.com/a/74355734
"sphinxcontrib-napoleon",
],
"journald": [
"systemd-python",
],
# FIXME technically this dependency is required, but in some cases we do not have access to
# the libalpm which is required in order to install the package. Thus in case if we do not
@ -140,9 +143,11 @@ setup(
],
"web": [
"Jinja2",
"aiohttp",
"aiohttp_jinja2",
"aioauth-client",
"aiohttp",
"aiohttp-apispec",
"aiohttp_cors",
"aiohttp_jinja2",
"aiohttp_debugtoolbar",
"aiohttp_session",
"aiohttp_security",

View File

@ -23,19 +23,20 @@ import sys
import tempfile
from pathlib import Path
from typing import List, TypeVar
from typing import TypeVar
from ahriman import version
from ahriman.application import handlers
from ahriman.core.util import enum_values
from ahriman.core.util import enum_values, extract_user
from ahriman.models.action import Action
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.log_handler import LogHandler
from ahriman.models.package_source import PackageSource
from ahriman.models.sign_settings import SignSettings
from ahriman.models.user_access import UserAccess
__all__: List[str] = []
__all__: list[str] = []
# this workaround is for several things
@ -58,6 +59,7 @@ def _formatter(prog: str) -> argparse.HelpFormatter:
return argparse.ArgumentDefaultsHelpFormatter(prog, width=120)
# pylint: disable=too-many-statements
def _parser() -> argparse.ArgumentParser:
"""
command line parser generator
@ -70,10 +72,14 @@ def _parser() -> argparse.ArgumentParser:
fromfile_prefix_chars="@", formatter_class=_formatter)
parser.add_argument("-a", "--architecture", help="target architectures. For several subcommands it can be used "
"multiple times", action="append")
parser.add_argument("-c", "--configuration", help="configuration path", type=Path, default=Path("/etc/ahriman.ini"))
parser.add_argument("-c", "--configuration", help="configuration path", type=Path,
default=Path("/etc") / "ahriman.ini")
parser.add_argument("--force", help="force run, remove file lock", action="store_true")
parser.add_argument("-l", "--lock", help="lock file", type=Path,
default=Path(tempfile.gettempdir()) / "ahriman.lock")
parser.add_argument("--log-handler", help="explicit log handler specification. If none set, the handler will be "
"guessed from environment",
type=LogHandler, choices=enum_values(LogHandler))
parser.add_argument("--report", help="force enable or disable reporting to web service",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-q", "--quiet", help="force disable any logging", action="store_true")
@ -99,6 +105,8 @@ def _parser() -> argparse.ArgumentParser:
_set_patch_set_add_parser(subparsers)
_set_repo_backup_parser(subparsers)
_set_repo_check_parser(subparsers)
_set_repo_create_keyring_parser(subparsers)
_set_repo_create_mirrorlist_parser(subparsers)
_set_repo_daemon_parser(subparsers)
_set_repo_rebuild_parser(subparsers)
_set_repo_remove_unknown_parser(subparsers)
@ -179,8 +187,8 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument
"""
parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
description="list unsafe commands as defined in default args", formatter_class=_formatter)
parser.add_argument("--command", help="instead of showing commands, just test command line for unsafe subcommand "
"and return 0 in case if command is safe and 1 otherwise")
parser.add_argument("command", help="instead of showing commands, just test command line for unsafe subcommand "
"and return 0 in case if command is safe and 1 otherwise", nargs="*")
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True, parser=_parser)
return parser
@ -254,6 +262,7 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
action="count", default=False)
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
parser.set_defaults(handler=handlers.Add)
return parser
@ -473,7 +482,46 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
"-yy to force refresh even if up to date",
action="count", default=False)
parser.set_defaults(handler=handlers.Update, dependencies=False, dry_run=True, aur=True, local=True, manual=False)
parser.set_defaults(handler=handlers.Update, dependencies=False, dry_run=True, aur=True, local=True, manual=False,
username=None)
return parser
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",
formatter_class=_formatter)
parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.support.KeyringTrigger"])
return parser
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",
formatter_class=_formatter)
parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.support.MirrorlistTrigger"])
return parser
@ -530,6 +578,9 @@ def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
"ahriman instance run with web service and have run repo-update at least once.",
action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-s", "--status", help="filter packages by status. Requires --from-database to be set",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
parser.set_defaults(handler=handlers.Rebuild)
return parser
@ -704,6 +755,7 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--manual", help="include or exclude manual updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
parser.add_argument("--vcs", help="fetch actual version of VCS packages",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
@ -822,7 +874,9 @@ def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
parser.add_argument("--build-command", help="build command prefix", default="ahriman")
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
type=Path, default=Path("/usr/share/devtools/pacman-extra.conf"))
type=Path, default=Path("/usr") / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
parser.add_argument("--generate-salt", help="generate salt for user passwords",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--makeflags-jobs", help="append MAKEFLAGS variable with parallelism set to number of cores",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--mirror", help="use the specified explicitly mirror instead of including mirrorlist")
@ -850,8 +904,7 @@ def _set_service_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("service-shell", aliases=["shell"], help="invoke python shell",
description="drop into python shell while having created application",
formatter_class=_formatter)
description="drop into python shell", formatter_class=_formatter)
parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
parser.set_defaults(handler=handlers.Shell, lock=None, report=False)
@ -871,15 +924,15 @@ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser = root.add_parser("user-add", help="create or update user",
description="update user for web services with the given password and role. "
"In case if password was not entered it will be asked interactively",
epilog="In case of first run (i.e. if password salt is not set yet) this action requires "
"root privileges because it performs write to filesystem configuration.",
formatter_class=_formatter)
parser.add_argument("username", help="username for web service")
parser.add_argument("--key", help="optional PGP key used by this user. The private key must be imported")
parser.add_argument("--packager", help="optional packager id used for build process in form of "
"`Name Surname <mail@example.com>`")
parser.add_argument("-p", "--password", help="user password. Blank password will be treated as empty password, "
"which is in particular must be used for OAuth2 authorization type.")
parser.add_argument("-r", "--role", help="user access level",
type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
parser.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, report=False,
quiet=True)
return parser
@ -901,8 +954,8 @@ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("username", help="filter users by username", nargs="?")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-r", "--role", help="filter users by role", type=UserAccess, choices=enum_values(UserAccess))
parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, report=False, # nosec
password="", quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, report=False,
quiet=True, unsafe=True)
return parser
@ -920,8 +973,8 @@ def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="remove user from the user mapping and update the configuration",
formatter_class=_formatter)
parser.add_argument("username", help="username for web service")
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False, # nosec
password="", quiet=True)
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False,
quiet=True)
return parser

View File

@ -17,10 +17,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from typing import Iterable, List, Set
from collections.abc import Callable, Iterable
from ahriman.application.application.application_packages import ApplicationPackages
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.core.formatters import UpdatePrinter
from ahriman.core.tree import Tree
from ahriman.models.package import Package
from ahriman.models.result import Result
@ -37,12 +39,12 @@ class Application(ApplicationPackages, ApplicationRepository):
>>> from ahriman.models.package_source import PackageSource
>>>
>>> configuration = Configuration()
>>> application = Application("x86_64", configuration, report=True, unsafe=False)
>>> application = Application("x86_64", configuration, report=True)
>>> # add packages to build queue
>>> application.add(["ahriman"], PackageSource.AUR, without_dependencies=False)
>>> application.add(["ahriman"], PackageSource.AUR)
>>>
>>> # check for updates
>>> updates = application.updates([], aur=True, local=True, manual=True, vcs=True, log_fn=print)
>>> updates = application.updates([], aur=True, local=True, manual=True, vcs=True)
>>> # updates for specified packages
>>> application.update(updates)
@ -50,14 +52,14 @@ class Application(ApplicationPackages, ApplicationRepository):
be used instead.
"""
def _known_packages(self) -> Set[str]:
def _known_packages(self) -> set[str]:
"""
load packages from repository and pacman repositories
Returns:
Set[str]: list of known packages
set[str]: list of known packages
"""
known_packages: Set[str] = set()
known_packages: set[str] = set()
# local set
for base in self.repository.packages():
for package, properties in base.packages.items():
@ -89,28 +91,48 @@ class Application(ApplicationPackages, ApplicationRepository):
"""
self.repository.triggers.on_stop()
def with_dependencies(self, packages: List[Package], *, process_dependencies: bool) -> List[Package]:
def print_updates(self, packages: list[Package], *, log_fn: Callable[[str], None]) -> None:
"""
print list of packages to be built. This method will build dependency tree and print updates accordingly
Args:
packages(list[Package]): package list to be printed
log_fn(Callable[[str], None]): logger function to log updates
"""
local_versions = {package.base: package.version for package in self.repository.packages()}
tree = Tree.resolve(packages)
for level in tree:
for package in level:
UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ")
def with_dependencies(self, packages: list[Package], *, process_dependencies: bool) -> list[Package]:
"""
add missing dependencies to list of packages
Args:
packages(List[Package]): list of source packages of which dependencies have to be processed
packages(list[Package]): list of source packages of which dependencies have to be processed
process_dependencies(bool): if no set, dependencies will not be processed
Returns:
list[Package]: updated packages list. Packager for dependencies will be copied from
original package
"""
def missing_dependencies(source: Iterable[Package]) -> Set[str]:
# build initial list of dependencies
result = set()
for package in source:
result.update(package.depends_build)
def missing_dependencies(source: Iterable[Package]) -> dict[str, str | None]:
# append list of known packages with packages which are in current sources
satisfied_packages = known_packages | {
single
for package in source
for single in package.packages_full
}
# remove ones which are already well-known
result = result.difference(known_packages)
# remove ones which are in this list already
for package in source:
result = result.difference(package.packages_full)
return result
return {
dependency: package.packager
for package in source
for dependency in package.depends_build
if dependency not in satisfied_packages
}
if not process_dependencies or not packages:
return packages
@ -119,8 +141,11 @@ class Application(ApplicationPackages, ApplicationRepository):
with_dependencies = {package.base: package for package in packages}
while missing := missing_dependencies(with_dependencies.values()):
for package_name in missing:
package = Package.from_aur(package_name, self.repository.pacman)
for package_name, username in missing.items():
package = Package.from_aur(package_name, self.repository.pacman, username)
with_dependencies[package.base] = package
# register package in local database
self.database.remote_update(package)
self.repository.reporter.set_unknown(package)
return list(with_dependencies.values())

View File

@ -20,11 +20,13 @@
import requests
import shutil
from collections.abc import Iterable
from pathlib import Path
from typing import Any, Iterable
from typing import Any
from ahriman.application.application.application_properties import ApplicationProperties
from ahriman.core.build_tools.sources import Sources
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.util import package_like
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
@ -42,20 +44,26 @@ class ApplicationPackages(ApplicationProperties):
Args:
source(str): path to package archive
Raises:
UnknownPackageError: if specified path doesn't exist
"""
local_path = Path(source)
if not local_path.is_file():
raise UnknownPackageError(source)
dst = self.repository.paths.packages / local_path.name
shutil.copy(local_path, dst)
def _add_aur(self, source: str) -> None:
def _add_aur(self, source: str, username: str | None) -> None:
"""
add package from AUR
Args:
source(str): package base name
username(str | None): optional override of username for build process
"""
package = Package.from_aur(source, self.repository.pacman)
package = Package.from_aur(source, self.repository.pacman, username)
self.database.build_queue_insert(package)
self.database.remote_update(package)
@ -67,21 +75,32 @@ class ApplicationPackages(ApplicationProperties):
source(str): path to local directory
"""
local_dir = Path(source)
if not local_dir.is_dir():
raise UnknownPackageError(source)
for full_path in filter(package_like, local_dir.iterdir()):
self._add_archive(str(full_path))
def _add_local(self, source: str) -> None:
def _add_local(self, source: str, username: str | None) -> None:
"""
add package from local PKGBUILDs
Args:
source(str): path to directory with local source files
username(str | None): optional override of username for build process
Raises:
UnknownPackageError: if specified package is unknown or doesn't exist
"""
source_dir = Path(source)
package = Package.from_build(source_dir, self.architecture)
cache_dir = self.repository.paths.cache_for(package.base)
shutil.copytree(source_dir, cache_dir) # copy package to store in caches
Sources.init(cache_dir) # we need to run init command in directory where we do have permissions
if (source_dir := Path(source)).is_dir():
package = Package.from_build(source_dir, self.architecture, username)
cache_dir = self.repository.paths.cache_for(package.base)
shutil.copytree(source_dir, cache_dir) # copy package to store in caches
Sources.init(cache_dir) # we need to run init command in directory where we do have permissions
elif (source_dir := self.repository.paths.cache_for(source)).is_dir():
package = Package.from_build(source_dir, self.architecture, username)
else:
raise UnknownPackageError(source)
self.database.build_queue_insert(package)
@ -93,36 +112,42 @@ class ApplicationPackages(ApplicationProperties):
source(str): remote URL of the package archive
"""
dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
response = requests.get(source, stream=True, timeout=None) # timeout=None to suppress pylint warns
response.raise_for_status()
# timeout=None to suppress pylint warns. Also suppress bandit warnings
try:
response = requests.get(source, stream=True, timeout=None) # nosec
response.raise_for_status()
except Exception:
raise UnknownPackageError(source)
with dst.open("wb") as local_file:
for chunk in response.iter_content(chunk_size=1024):
local_file.write(chunk)
def _add_repository(self, source: str, *_: Any) -> None:
def _add_repository(self, source: str, username: str | None) -> None:
"""
add package from official repository
Args:
source(str): package base name
username(str | None): optional override of username for build process
"""
package = Package.from_official(source, self.repository.pacman)
package = Package.from_official(source, self.repository.pacman, username)
self.database.build_queue_insert(package)
self.database.remote_update(package)
def add(self, names: Iterable[str], source: PackageSource) -> None:
def add(self, names: Iterable[str], source: PackageSource, username: str | None = None) -> None:
"""
add packages for the next build
Args:
names(Iterable[str]): list of package bases to add
source(PackageSource): package source to add
username(str | None, optional): optional override of username for build process (Default value = None)
"""
for name in names:
resolved_source = source.resolve(name)
fn = getattr(self, f"_add_{resolved_source.value}")
fn(name)
fn(name, username)
def on_result(self, result: Result) -> None:
"""

View File

@ -21,6 +21,7 @@ from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.log import LazyLogging
from ahriman.core.repository import Repository
from ahriman.models.pacman_synchronization import PacmanSynchronization
class ApplicationProperties(LazyLogging):
@ -34,8 +35,8 @@ class ApplicationProperties(LazyLogging):
repository(Repository): repository instance
"""
def __init__(self, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
def __init__(self, architecture: str, configuration: Configuration, *, report: bool,
refresh_pacman_database: PacmanSynchronization = PacmanSynchronization.Disabled) -> None:
"""
default constructor
@ -43,12 +44,11 @@ class ApplicationProperties(LazyLogging):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
refresh_pacman_database(int, optional): pacman database syncronization level, ``0`` is disabled
(Default value = 0)
refresh_pacman_database(PacmanSynchronization, optional): pacman database synchronization level
(Default value = PacmanSynchronization.Disabled)
"""
self.configuration = configuration
self.architecture = architecture
self.database = SQLite.load(configuration)
self.repository = Repository.load(architecture, configuration, self.database, report=report, unsafe=unsafe,
self.repository = Repository.load(architecture, configuration, self.database, report=report,
refresh_pacman_database=refresh_pacman_database)

View File

@ -17,14 +17,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from collections.abc import Iterable
from pathlib import Path
from typing import Callable, Iterable, List
from ahriman.application.application.application_properties import ApplicationProperties
from ahriman.core.build_tools.sources import Sources
from ahriman.core.formatters import UpdatePrinter
from ahriman.core.tree import Tree
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
@ -83,35 +83,35 @@ class ApplicationRepository(ApplicationProperties):
if archive.filepath is None:
self.logger.warning("filepath is empty for %s", package.base)
continue # avoid mypy warning
self.repository.sign.process_sign_package(archive.filepath, package.base)
self.repository.sign.process_sign_package(archive.filepath, None)
# sign repository database if set
self.repository.sign.process_sign_repository(self.repository.repo.repo_path)
# process triggers
self.on_result(Result())
def unknown(self) -> List[str]:
def unknown(self) -> list[str]:
"""
get packages which were not found in AUR
Returns:
List[str]: unknown package archive list
list[str]: unknown package archive list
"""
def has_local(probe: Package) -> bool:
cache_dir = self.repository.paths.cache_for(probe.base)
return cache_dir.is_dir() and not Sources.has_remotes(cache_dir)
def unknown_aur(probe: Package) -> List[str]:
packages: List[str] = []
def unknown_aur(probe: Package) -> list[str]:
packages: list[str] = []
for single in probe.packages:
try:
_ = Package.from_aur(single, self.repository.pacman)
_ = Package.from_aur(single, self.repository.pacman, None)
except Exception:
packages.append(single)
return packages
def unknown_local(probe: Package) -> List[str]:
def unknown_local(probe: Package) -> list[str]:
cache_dir = self.repository.paths.cache_for(probe.base)
local = Package.from_build(cache_dir, self.architecture)
local = Package.from_build(cache_dir, self.architecture, None)
packages = set(probe.packages.keys()).difference(local.packages.keys())
return list(packages)
@ -123,12 +123,14 @@ class ApplicationRepository(ApplicationProperties):
result.extend(unknown_aur(package)) # local package not found
return result
def update(self, updates: Iterable[Package]) -> Result:
def update(self, updates: Iterable[Package], packagers: Packagers | None = None) -> Result:
"""
run package updates
Args:
updates(Iterable[Package]): list of packages to update
packagers(Packagers | None, optional): optional override of username for build process
(Default value = None)
Returns:
Result: update result
@ -136,7 +138,7 @@ class ApplicationRepository(ApplicationProperties):
def process_update(paths: Iterable[Path], result: Result) -> None:
if not paths:
return # don't need to process if no update supplied
update_result = self.repository.process_update(paths)
update_result = self.repository.process_update(paths, packagers)
self.on_result(result.merge(update_result))
# process built packages
@ -148,14 +150,14 @@ class ApplicationRepository(ApplicationProperties):
tree = Tree.resolve(updates)
for num, level in enumerate(tree):
self.logger.info("processing level #%i %s", num, [package.base for package in level])
build_result = self.repository.process_build(level)
build_result = self.repository.process_build(level, packagers)
packages = self.repository.packages_built()
process_update(packages, build_result)
return build_result
def updates(self, filter_packages: Iterable[str], *,
aur: bool, local: bool, manual: bool, vcs: bool, log_fn: Callable[[str], None]) -> List[Package]:
aur: bool, local: bool, manual: bool, vcs: bool) -> list[Package]:
"""
get list of packages to run update process
@ -165,10 +167,9 @@ class ApplicationRepository(ApplicationProperties):
local(bool): enable or disable checking of local packages for updates
manual(bool): include or exclude manual updates
vcs(bool): enable or disable checking of VCS packages
log_fn(Callable[[str], None]): logger function to log updates
Returns:
List[Package]: list of out-of-dated packages
list[Package]: list of out-of-dated packages
"""
updates = {}
@ -179,14 +180,4 @@ class ApplicationRepository(ApplicationProperties):
if manual:
updates.update({package.base: package for package in self.repository.updates_manual()})
local_versions = {package.base: package.version for package in self.repository.packages()}
updated_packages = [package for _, package in sorted(updates.items())]
# reorder updates according to the dependency tree
tree = Tree.resolve(updated_packages)
for level in tree:
for package in level:
UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ")
return updated_packages
return [package for _, package in sorted(updates.items())]

View File

@ -19,11 +19,10 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.packagers import Packagers
class Add(Handler):
@ -32,8 +31,7 @@ class Add(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -42,17 +40,17 @@ class Add(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration,
report=report, unsafe=unsafe, refresh_pacman_database=args.refresh)
application = Application(architecture, configuration, report=report, refresh_pacman_database=args.refresh)
application.on_start()
application.add(args.package, args.source)
application.add(args.package, args.source, args.username)
if not args.now:
return
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False,
log_fn=application.logger.info)
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False)
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
result = application.update(packages)
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
application.print_updates(packages, log_fn=application.logger.info)
result = application.update(packages, packagers)
Add.check_if_empty(args.exit_code, result.is_empty)

View File

@ -22,7 +22,6 @@ import pwd
from pathlib import Path
from tarfile import TarFile
from typing import Set, Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -37,8 +36,7 @@ class Backup(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -47,7 +45,6 @@ class Backup(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
backup_paths = Backup.get_paths(configuration)
with TarFile(args.path, mode="w") as archive: # well we don't actually use compression
@ -55,7 +52,7 @@ class Backup(Handler):
archive.add(backup_path)
@staticmethod
def get_paths(configuration: Configuration) -> Set[Path]:
def get_paths(configuration: Configuration) -> set[Path]:
"""
extract paths to back up
@ -63,7 +60,7 @@ class Backup(Handler):
configuration(Configuration): configuration instance
Returns:
Set[Path]: map of the filesystem paths
set[Path]: map of the filesystem paths
"""
paths = set(configuration.include.glob("*.ini"))

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -32,8 +30,7 @@ class Clean(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -42,9 +39,8 @@ class Clean(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
application.on_start()
application.clean(cache=args.cache, chroot=args.chroot, manual=args.manual, packages=args.packages,
pacman=args.pacman)

View File

@ -20,8 +20,6 @@
import argparse
import threading
from typing import Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -32,8 +30,7 @@ class Daemon(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -42,11 +39,10 @@ class Daemon(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
from ahriman.application.handlers import Update
Update.run(args, architecture, configuration, report=report, unsafe=unsafe)
Update.run(args, architecture, configuration, report=report)
timer = threading.Timer(args.interval, Daemon.run, args=[args, architecture, configuration],
kwargs={"report": report, "unsafe": unsafe})
kwargs={"report": report})
timer.start()
timer.join()

View File

@ -19,11 +19,9 @@
#
import argparse
from typing import Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ConfigurationPrinter
from ahriman.core.formatters import ConfigurationPathsPrinter, ConfigurationPrinter, StringPrinter
class Dump(Handler):
@ -34,8 +32,7 @@ class Dump(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -44,8 +41,13 @@ class Dump(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
root, _ = configuration.check_loaded()
ConfigurationPathsPrinter(root, configuration.includes).print(verbose=True, separator=" = ")
# empty line
StringPrinter("").print(verbose=False)
dump = configuration.dump()
for section, values in sorted(dump.items()):
ConfigurationPrinter(section, values).print(verbose=not args.secure, separator=" = ")

View File

@ -17,13 +17,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
import argparse
import logging
from multiprocessing import Pool
from typing import List, Type
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
@ -53,7 +50,7 @@ class Handler:
ALLOW_MULTI_ARCHITECTURE_RUN = True
@classmethod
def architectures_extract(cls: Type[Handler], args: argparse.Namespace) -> List[str]:
def architectures_extract(cls, args: argparse.Namespace) -> list[str]:
"""
get known architectures
@ -61,7 +58,7 @@ class Handler:
args(argparse.Namespace): command line args
Returns:
List[str]: list of architectures for which tree is created
list[str]: list of architectures for which tree is created
Raises:
MissingArchitecture: if no architecture set and automatic detection is not allowed or failed
@ -84,7 +81,7 @@ class Handler:
return sorted(architectures)
@classmethod
def call(cls: Type[Handler], args: argparse.Namespace, architecture: str) -> bool:
def call(cls, args: argparse.Namespace, architecture: str) -> bool:
"""
additional function to wrap all calls for multiprocessing library
@ -97,9 +94,10 @@ class Handler:
"""
try:
configuration = Configuration.from_path(args.configuration, architecture)
Log.load(configuration, quiet=args.quiet, report=args.report)
log_handler = Log.handler(args.log_handler)
Log.load(configuration, log_handler, quiet=args.quiet, report=args.report)
with Lock(args, architecture, configuration):
cls.run(args, architecture, configuration, report=args.report, unsafe=args.unsafe)
cls.run(args, architecture, configuration, report=args.report)
return True
except ExitCode:
return False
@ -109,7 +107,7 @@ class Handler:
return False
@classmethod
def execute(cls: Type[Handler], args: argparse.Namespace) -> int:
def execute(cls, args: argparse.Namespace) -> int:
"""
execute function for all aru
@ -138,8 +136,7 @@ class Handler:
return 0 if all(result) else 1
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -148,7 +145,6 @@ class Handler:
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
Raises:
NotImplementedError: not implemented method

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -33,8 +31,7 @@ class Help(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -43,7 +40,6 @@ class Help(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
parser: argparse.ArgumentParser = args.parser()
if args.command is None:

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -34,8 +32,7 @@ class KeyImport(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -44,7 +41,6 @@ class KeyImport(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
application.repository.sign.key_import(args.key_server, args.key)

View File

@ -21,7 +21,6 @@ import argparse
import sys
from pathlib import Path
from typing import List, Optional, Tuple, Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
@ -39,8 +38,7 @@ class Patch(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -49,9 +47,8 @@ class Patch(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
application.on_start()
if args.action == Action.Update and args.variable is not None:
@ -66,31 +63,31 @@ class Patch(Handler):
Patch.patch_set_remove(application, args.package, args.variable)
@staticmethod
def patch_create_from_diff(sources_dir: Path, architecture: str, track: List[str]) -> Tuple[str, PkgbuildPatch]:
def patch_create_from_diff(sources_dir: Path, architecture: str, track: list[str]) -> tuple[str, PkgbuildPatch]:
"""
create PKGBUILD plain diff patches from sources directory
Args:
sources_dir(Path): path to directory with the package sources
architecture(str): repository architecture
track(List[str]): track files which match the glob before creating the patch
track(list[str]): track files which match the glob before creating the patch
Returns:
Tuple[str, PkgbuildPatch]: package base and created PKGBUILD patch based on the diff from master HEAD
tuple[str, PkgbuildPatch]: package base and created PKGBUILD patch based on the diff from master HEAD
to current files
"""
package = Package.from_build(sources_dir, architecture)
package = Package.from_build(sources_dir, architecture, None)
patch = Sources.patch_create(sources_dir, *track)
return package.base, PkgbuildPatch(None, patch)
@staticmethod
def patch_create_from_function(variable: str, patch_path: Optional[Path]) -> PkgbuildPatch:
def patch_create_from_function(variable: str, patch_path: Path | None) -> PkgbuildPatch:
"""
create single-function patch set for the package base
Args:
variable(str): function or variable name inside PKGBUILD
patch_path(Path): optional path to patch content. If not set, it will be read from stdin
patch_path(Path | None): optional path to patch content. If not set, it will be read from stdin
Returns:
PkgbuildPatch: created patch for the PKGBUILD function
@ -116,15 +113,15 @@ class Patch(Handler):
application.database.patches_insert(package_base, patch)
@staticmethod
def patch_set_list(application: Application, package_base: Optional[str], variables: List[str],
def patch_set_list(application: Application, package_base: str | None, variables: list[str],
exit_code: bool) -> None:
"""
list patches available for the package base
Args:
application(Application): application instance
package_base(Optional[str]): package base
variables(List[str]): extract patches only for specified PKGBUILD variables
package_base(str | None): package base
variables(list[str]): extract patches only for specified PKGBUILD variables
exit_code(bool): exit with error on empty search result
"""
patches = application.database.patches_list(package_base, variables)
@ -134,13 +131,13 @@ class Patch(Handler):
PatchPrinter(base, patch).print(verbose=True, separator=" = ")
@staticmethod
def patch_set_remove(application: Application, package_base: str, variables: List[str]) -> None:
def patch_set_remove(application: Application, package_base: str, variables: list[str]) -> None:
"""
remove patch set for the package base
Args:
application(Application): application instance
package_base(str): package base
variables(List[str]): remove patches only for specified PKGBUILD variables
variables(list[str]): remove patches only for specified PKGBUILD variables
"""
application.database.patches_remove(package_base, variables)

View File

@ -19,12 +19,10 @@
#
import argparse
from typing import List, Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import UpdatePrinter
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
@ -34,8 +32,7 @@ class Rebuild(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -44,37 +41,40 @@ class Rebuild(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
depends_on = set(args.depends_on) if args.depends_on else None
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
application.on_start()
packages = Rebuild.extract_packages(application, from_database=args.from_database)
updates = application.repository.packages_depend_on(packages, depends_on)
packages = Rebuild.extract_packages(application, args.status, from_database=args.from_database)
updates = application.repository.packages_depend_on(packages, args.depends_on or None)
Rebuild.check_if_empty(args.exit_code, not updates)
if args.dry_run:
for package in updates:
UpdatePrinter(package, package.version).print(verbose=True)
application.print_updates(updates, log_fn=print)
return
result = application.update(updates)
result = application.update(updates, args.username)
Rebuild.check_if_empty(args.exit_code, result.is_empty)
@staticmethod
def extract_packages(application: Application, *, from_database: bool) -> List[Package]:
def extract_packages(application: Application, status: BuildStatusEnum | None, *,
from_database: bool) -> list[Package]:
"""
extract packages from database file
Args:
application(Application): application instance
status(BuildStatusEnum | None): optional filter by package status
from_database(bool): extract packages from database instead of repository filesystem
Returns:
List[Package]: list of packages which were stored in database
list[Package]: list of packages which were stored in database
"""
if from_database:
return [package for (package, _) in application.database.packages_get()]
return [
package
for (package, last_status) in application.database.packages_get()
if status is None or last_status.status == status
]
return application.repository.packages()

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -32,8 +30,7 @@ class Remove(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -42,8 +39,7 @@ class Remove(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
application.on_start()
application.remove(args.package)

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -33,8 +31,7 @@ class RemoveUnknown(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -43,9 +40,8 @@ class RemoveUnknown(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
application.on_start()
unknown_packages = application.unknown()

View File

@ -19,7 +19,6 @@
#
import argparse
from typing import Type
from tarfile import TarFile
from ahriman.application.handlers import Handler
@ -34,8 +33,7 @@ class Restore(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -44,7 +42,6 @@ class Restore(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
with TarFile(args.path) as archive:
archive.extractall(path=args.output)

View File

@ -20,7 +20,7 @@
import argparse
from dataclasses import fields
from typing import Callable, Iterable, List, Tuple, Type
from collections.abc import Callable, Iterable
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
@ -36,15 +36,18 @@ class Search(Handler):
packages search handler
Attributes:
SORT_FIELDS(Set[str]): (class attribute) allowed fields to sort the package list
SORT_FIELDS(set[str]): (class attribute) allowed fields to sort the package list
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list} # type: ignore
SORT_FIELDS = {
field.name
for field in fields(AURPackage)
if field.default_factory is not list # type: ignore[comparison-overlap]
}
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -53,9 +56,8 @@ class Search(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
official_packages_list = Official.multisearch(*args.search, pacman=application.repository.pacman)
aur_packages_list = AUR.multisearch(*args.search, pacman=application.repository.pacman)
@ -67,7 +69,7 @@ class Search(Handler):
AurPrinter(package).print(verbose=args.info)
@staticmethod
def sort(packages: Iterable[AURPackage], sort_by: str) -> List[AURPackage]:
def sort(packages: Iterable[AURPackage], sort_by: str) -> list[AURPackage]:
"""
sort package list by specified field
@ -76,7 +78,7 @@ class Search(Handler):
sort_by(str): AUR package field name to sort by
Returns:
List[AURPackage]: sorted list for packages
list[AURPackage]: sorted list for packages
Raises:
InvalidOption: if search fields is not in list of allowed ones
@ -85,6 +87,6 @@ class Search(Handler):
raise OptionError(sort_by)
# always sort by package name at the last
# well technically it is not a string, but we can deal with it
comparator: Callable[[AURPackage], Tuple[str, str]] =\
comparator: Callable[[AURPackage], tuple[str, str]] =\
lambda package: (getattr(package, sort_by), package.name)
return sorted(packages, key=comparator)

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman import version
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
@ -37,8 +35,7 @@ class ServiceUpdates(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -47,11 +44,10 @@ class ServiceUpdates(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
remote = Package.from_aur("ahriman", application.repository.pacman)
remote = Package.from_aur("ahriman", application.repository.pacman, None)
release = remote.version.rsplit("-", 1)[-1] # we don't store pkgrel locally, so we just append it
local_version = f"{version.__version__}-{release}"

View File

@ -21,12 +21,12 @@ import argparse
from pathlib import Path
from pwd import getpwuid
from typing import Optional, Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.user import User
class Setup(Handler):
@ -41,13 +41,12 @@ class Setup(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
ARCHBUILD_COMMAND_PATH = Path("/usr/bin/archbuild")
MIRRORLIST_PATH = Path("/etc/pacman.d/mirrorlist")
SUDOERS_DIR_PATH = Path("/etc/sudoers.d")
ARCHBUILD_COMMAND_PATH = Path("/usr") / "bin" / "archbuild"
MIRRORLIST_PATH = Path("/etc") / "pacman.d" / "mirrorlist"
SUDOERS_DIR_PATH = Path("/etc") / "sudoers.d"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -56,12 +55,11 @@ class Setup(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration)
configuration.reload()
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
Setup.executable_create(application.repository.paths, args.build_command, architecture)
@ -127,24 +125,27 @@ class Setup(Handler):
if args.web_unix_socket is not None:
configuration.set_option(section, "unix_socket", str(args.web_unix_socket))
if args.generate_salt:
configuration.set_option("auth", "salt", User.generate_password(20))
target = root.include / "00-setup-overrides.ini"
with target.open("w") as ahriman_configuration:
configuration.write(ahriman_configuration)
@staticmethod
def configuration_create_devtools(prefix: str, architecture: str, source: Path, mirror: Optional[str],
def configuration_create_devtools(prefix: str, architecture: str, source: Path, mirror: str | None,
multilib: bool, repository: str, paths: RepositoryPaths) -> None:
"""
create configuration for devtools based on ``source`` configuration
Note:
Notes:
devtools does not allow to specify the pacman configuration, thus we still have to use configuration in /usr
Args:
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture
source(Path): path to source configuration file
mirror(Optional[str]): link to package server mirror
mirror(str | None): link to package server mirror
multilib(bool): add or do not multilib repository to the configuration
repository(str): repository name
paths(RepositoryPaths): repository paths instance
@ -154,7 +155,7 @@ class Setup(Handler):
configuration = Configuration(allow_no_value=True)
# preserve case
# stupid mypy thinks that it is impossible
configuration.optionxform = lambda key: key # type: ignore
configuration.optionxform = lambda key: key # type: ignore[method-assign]
# load default configuration first
# we cannot use Include here because it will be copied to new chroot, thus no includes there
@ -176,10 +177,10 @@ class Setup(Handler):
configuration.set_option(section, "Server", mirror)
# add repository itself
configuration.set_option(repository, "SigLevel", "Optional TrustAll") # we don't care
configuration.set_option(repository, "SigLevel", "Never") # we don't care
configuration.set_option(repository, "Server", f"file://{paths.repository}")
target = source.parent / f"pacman-{prefix}-{architecture}.conf"
target = source.parent / f"{prefix}-{architecture}.conf"
with target.open("w") as devtools_configuration:
configuration.write(devtools_configuration)
@ -214,7 +215,7 @@ class Setup(Handler):
"""
command = Setup.build_command(paths.root, prefix, architecture)
sudoers_file = Setup.build_command(Setup.SUDOERS_DIR_PATH, prefix, architecture)
sudoers_file.write_text(f"ahriman ALL=(ALL) NOPASSWD: {command} *\n", encoding="utf8")
sudoers_file.write_text(f"ahriman ALL=(ALL) NOPASSWD:SETENV: {command} *\n", encoding="utf8")
sudoers_file.chmod(0o400) # security!
@staticmethod

View File

@ -22,9 +22,7 @@ import code
import sys
from pathlib import Path
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
@ -38,8 +36,7 @@ class Shell(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -48,16 +45,13 @@ class Shell(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# pylint: disable=possibly-unused-variable
application = Application(architecture, configuration, report=report, unsafe=unsafe)
if args.verbose:
# licensed by https://creativecommons.org/licenses/by-sa/3.0
path = Path(sys.prefix) / "share" / "ahriman" / "templates" / "shell"
StringPrinter(path.read_text(encoding="utf8")).print(verbose=False)
# we only want to pass application isntance inside
local_variables = {"architecture": architecture, "configuration": configuration}
if args.code is None:
code.interact(local={"application": application})
code.interact(local=local_variables)
else:
code.InteractiveConsole(locals={"application": application}).runcode(args.code)
code.InteractiveConsole(locals=local_variables).runcode(args.code)

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -32,8 +30,7 @@ class Sign(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -42,6 +39,5 @@ class Sign(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
Application(architecture, configuration, report=report, unsafe=unsafe).sign(args.package)
Application(architecture, configuration, report=report).sign(args.package)

View File

@ -19,7 +19,7 @@
#
import argparse
from typing import Callable, Iterable, Tuple, Type
from collections.abc import Callable
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
@ -37,8 +37,7 @@ class Status(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -47,15 +46,14 @@ class Status(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# we are using reporter here
client = Application(architecture, configuration, report=True, unsafe=unsafe).repository.reporter
client = Application(architecture, configuration, report=True).repository.reporter
if args.ahriman:
service_status = client.get_internal()
StatusPrinter(service_status.status).print(verbose=args.info)
if args.package:
packages: Iterable[Tuple[Package, BuildStatus]] = sum(
packages: list[tuple[Package, BuildStatus]] = sum(
(client.get(base) for base in args.package),
start=[])
else:
@ -63,8 +61,8 @@ class Status(Handler):
Status.check_if_empty(args.exit_code, not packages)
comparator: Callable[[Tuple[Package, BuildStatus]], str] = lambda item: item[0].base
filter_fn: Callable[[Tuple[Package, BuildStatus]], bool] =\
comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda item: item[0].base
filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\
lambda item: args.status is None or item[1].status == args.status
for package, package_status in sorted(filter(filter_fn, packages), key=comparator):
PackagePrinter(package, package_status).print(verbose=args.info)

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -35,8 +33,7 @@ class StatusUpdate(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -45,10 +42,9 @@ class StatusUpdate(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# we are using reporter here
client = Application(architecture, configuration, report=True, unsafe=unsafe).repository.reporter
client = Application(architecture, configuration, report=True).repository.reporter
if args.action == Action.Update and args.package:
# update packages statuses

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -36,8 +34,7 @@ class Structure(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -46,9 +43,8 @@ class Structure(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
packages = application.repository.packages()
tree = Tree.resolve(packages)

View File

@ -19,8 +19,6 @@
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -33,8 +31,7 @@ class Triggers(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -43,9 +40,8 @@ class Triggers(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
application = Application(architecture, configuration, report=report)
if args.trigger:
loader = application.repository.triggers
loader.triggers = [loader.load_trigger(trigger, architecture, configuration) for trigger in args.trigger]

View File

@ -18,9 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
import shlex
from typing import List, Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -35,8 +32,7 @@ class UnsafeCommands(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -45,31 +41,30 @@ class UnsafeCommands(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
parser = args.parser()
unsafe_commands = UnsafeCommands.get_unsafe_commands(parser)
if args.command is None:
if args.command:
UnsafeCommands.check_unsafe(args.command, unsafe_commands, parser)
else:
for command in unsafe_commands:
StringPrinter(command).print(verbose=True)
else:
UnsafeCommands.check_unsafe(args.command, unsafe_commands, parser)
@staticmethod
def check_unsafe(command: str, unsafe_commands: List[str], parser: argparse.ArgumentParser) -> None:
def check_unsafe(command: list[str], unsafe_commands: list[str], parser: argparse.ArgumentParser) -> None:
"""
check if command is unsafe
Args:
command(str): command to check
unsafe_commands(List[str]): list of unsafe commands
unsafe_commands(list[str]): list of unsafe commands
parser(argparse.ArgumentParser): generated argument parser
"""
args = parser.parse_args(shlex.split(command))
args = parser.parse_args(command)
UnsafeCommands.check_if_empty(True, args.command in unsafe_commands)
@staticmethod
def get_unsafe_commands(parser: argparse.ArgumentParser) -> List[str]:
def get_unsafe_commands(parser: argparse.ArgumentParser) -> list[str]:
"""
extract unsafe commands from argument parser
@ -77,9 +72,10 @@ class UnsafeCommands(Handler):
parser(argparse.ArgumentParser): generated argument parser
Returns:
List[str]: list of commands with default unsafe flag
list[str]: list of commands with default unsafe flag
"""
# should never fail
# pylint: disable=protected-access
subparser = next(action for action in parser._actions if isinstance(action, argparse._SubParsersAction))
return sorted(action_name for action_name, action in subparser.choices.items() if action.get_default("unsafe"))
subparser = next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
actions = subparser.choices if subparser is not None else {}
return sorted(action_name for action_name, action in actions.items() if action.get_default("unsafe"))

View File

@ -19,11 +19,12 @@
#
import argparse
from typing import Callable, Type
from collections.abc import Callable
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.packagers import Packagers
class Update(Handler):
@ -32,8 +33,7 @@ class Update(Handler):
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -42,19 +42,19 @@ class Update(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe,
refresh_pacman_database=args.refresh)
application = Application(architecture, configuration, report=report, refresh_pacman_database=args.refresh)
application.on_start()
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
log_fn=Update.log_fn(application, args.dry_run))
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
Update.check_if_empty(args.exit_code, not packages)
if args.dry_run:
return
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
result = application.update(packages)
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
application.print_updates(packages, log_fn=application.logger.info)
result = application.update(packages, packagers)
Update.check_if_empty(args.exit_code, result.is_empty)
@staticmethod

View File

@ -20,9 +20,6 @@
import argparse
import getpass
from pathlib import Path
from typing import Optional, Tuple, Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
@ -40,8 +37,7 @@ class Users(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -50,18 +46,13 @@ class Users(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
database = SQLite.load(configuration)
if args.action == Action.Update:
old_salt, salt = Users.get_salt(configuration)
user = Users.user_create(args)
if old_salt is None:
auth_configuration = Users.configuration_get(configuration.include)
Users.configuration_create(auth_configuration, salt, args.secure)
# if password is left blank we are not going to require salt to be set
salt = configuration.get("auth", "salt") if user.password else ""
database.user_update(user.hash_password(salt))
elif args.action == Action.List:
users = database.user_list(args.username, args.role)
@ -71,70 +62,6 @@ class Users(Handler):
elif args.action == Action.Remove:
database.user_remove(args.username)
@staticmethod
def configuration_create(configuration: Configuration, salt: str, secure: bool) -> None:
"""
enable configuration if it has been disabled
Args:
configuration(Configuration): configuration instance
salt(str): password hash salt
secure(bool): if true then set file permissions to 0o600
"""
configuration.set_option("auth", "salt", salt)
Users.configuration_write(configuration, secure)
@staticmethod
def configuration_get(include_path: Path) -> Configuration:
"""
create configuration instance
Args:
include_path(Path): path to directory with configuration includes
Returns:
Configuration: configuration instance. In case if there are local settings they will be loaded
"""
target = include_path / "00-auth.ini"
configuration = Configuration()
configuration.load(target)
configuration.architecture = "" # not user anyway
return configuration
@staticmethod
def configuration_write(configuration: Configuration, secure: bool) -> None:
"""
write configuration file
Args:
configuration(Configuration): configuration instance
secure(bool): if true then set file permissions to 0o600
"""
path, _ = configuration.check_loaded()
with path.open("w") as ahriman_configuration:
configuration.write(ahriman_configuration)
if secure:
path.chmod(0o600)
@staticmethod
def get_salt(configuration: Configuration, salt_length: int = 20) -> Tuple[Optional[str], str]:
"""
get salt from configuration or create new string
Args:
configuration(Configuration): configuration instance
salt_length(int, optional): salt length (Default value = 20)
Returns:
Tuple[Optional[str], str]: tuple containing salt from configuration if any and actual salt which must be
used for password hash
"""
if salt := configuration.get("auth", "salt", fallback=None):
return salt, salt
return None, User.generate_password(salt_length)
@staticmethod
def user_create(args: argparse.Namespace) -> User:
"""
@ -157,4 +84,5 @@ class Users(Handler):
if password is None:
password = read_password()
return User(username=args.username, password=password, access=args.role)
return User(username=args.username, password=password, access=args.role,
packager_id=args.packager, key=args.key)

View File

@ -20,7 +20,7 @@
import argparse
import copy
from typing import Any, Dict, Type
from typing import Any
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -39,8 +39,7 @@ class Validate(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -49,7 +48,6 @@ class Validate(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
schema = Validate.schema(architecture, configuration)
validator = Validator(configuration=configuration, schema=schema)
@ -78,7 +76,9 @@ class Validate(Handler):
# create trigger loader instance
loader = TriggerLoader()
for trigger in loader.selected_triggers(configuration):
triggers = loader.selected_triggers(configuration) + loader.known_triggers(configuration)
for trigger in set(triggers):
try:
trigger_class = loader.load_trigger_class(trigger)
except ExtensionError:
@ -112,17 +112,17 @@ class Validate(Handler):
return schema
@staticmethod
def schema_merge(source: Dict[str, Any], schema: Dict[str, Any]) -> Dict[str, Any]:
def schema_merge(source: dict[str, Any], schema: dict[str, Any]) -> dict[str, Any]:
"""
merge child schema into source. In case if source already contains values, new keys will be added
(possibly with overrides - in case if such key already set also)
Args:
source(Dict[str, Any]): source (current) schema into which will be merged
schema(Dict[str, Any]): new schema to be merged
source(dict[str, Any]): source (current) schema into which will be merged
schema(dict[str, Any]): new schema to be merged
Returns:
Dict[str, Any]: schema with added elements from source schema if they were set before and not presented
dict[str, Any]: schema with added elements from source schema if they were set before and not presented
in the new one. Note, that schema will be modified in-place
"""
for key, value in source.items():

View File

@ -18,10 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
import pkg_resources
import re
import sys
from typing import Dict, List, Tuple, Type
from collections.abc import Generator
from importlib import metadata
from ahriman import version
from ahriman.application.handlers import Handler
@ -32,13 +33,16 @@ from ahriman.core.formatters import VersionPrinter
class Versions(Handler):
"""
version handler
Attributes:
PEP423_PACKAGE_NAME(str): (class attribute) special regex for valid PEP423 package name
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
PEP423_PACKAGE_NAME = re.compile(r"^[A-Za-z0-9._-]+")
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -47,41 +51,46 @@ class Versions(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
VersionPrinter(f"Module version {version.__version__}",
{"Python": sys.version}).print(verbose=False, separator=" ")
packages = Versions.package_dependencies("ahriman", ("pacman", "s3", "web"))
VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ")
packages = Versions.package_dependencies("ahriman")
VersionPrinter("Installed packages", dict(packages)).print(verbose=False, separator=" ")
@staticmethod
def package_dependencies(root: str, root_extras: Tuple[str, ...] = ()) -> Dict[str, str]:
def package_dependencies(root: str) -> Generator[tuple[str, str], None, None]:
"""
extract list of ahriman package dependencies installed into system with their versions
Args:
root(str): root package name
root_extras(Tuple[str, ...], optional): extras for the root package (Default value = ())
Returns:
Dict[str, str]: map of installed dependency to its version
Generator[tuple[str, str], None, None]: map of installed dependency to its version
"""
resources: Dict[str, pkg_resources.Distribution] = pkg_resources.working_set.by_key # type: ignore
def dependencies_by_key(key: str) -> Generator[str, None, None]:
# in importlib it returns requires in the following format
# ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
try:
requires = metadata.requires(key)
except metadata.PackageNotFoundError:
return
for entry in requires or []:
yield from Versions.PEP423_PACKAGE_NAME.findall(entry)
def dependencies_by_key(key: str, extras: Tuple[str, ...] = ()) -> List[str]:
return [entry.key for entry in resources[key].requires(extras)]
keys: List[str] = []
portion = {key for key in dependencies_by_key(root, root_extras) if key in resources}
keys: list[str] = []
portion = set(dependencies_by_key(root))
while portion:
keys.extend(portion)
portion = {
key
for key in sum((dependencies_by_key(key) for key in portion), start=[])
if key not in keys and key in resources
for key in sum((list(dependencies_by_key(key)) for key in portion), start=[])
if key not in keys
}
return {
resource.project_name: resource.version
for resource in map(lambda key: resources[key], keys)
}
for key in keys:
try:
distribution = metadata.distribution(key)
yield distribution.name, distribution.version
except metadata.PackageNotFoundError:
continue

View File

@ -19,7 +19,7 @@
#
import argparse
from typing import Type
from collections.abc import Generator
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
@ -33,10 +33,10 @@ class Web(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
COMMAND_ARGS_WHITELIST = ["force", "log_handler", ""]
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
@ -45,12 +45,12 @@ class Web(Handler):
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
# we are using local import for optional dependencies
from ahriman.web.web import run_server, setup_service
spawner = Spawn(args.parser(), architecture, configuration)
spawner_args = Web.extract_arguments(args, architecture, configuration)
spawner = Spawn(args.parser(), architecture, list(spawner_args))
spawner.start()
application = setup_service(architecture, configuration, spawner)
@ -59,3 +59,33 @@ class Web(Handler):
# terminate spawn process at the last
spawner.stop()
spawner.join()
@staticmethod
def extract_arguments(args: argparse.Namespace, architecture: str,
configuration: Configuration) -> Generator[str, None, None]:
"""
extract list of arguments used for current command, except for command specific ones
Args:
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
Returns:
Generator[str, None, None]: command line arguments which were used for this specific command
"""
# read architecture from the same argument list
yield from ["--architecture", architecture]
# read configuration path from current settings
if (configuration_path := configuration.path) is not None:
yield from ["--configuration", str(configuration_path)]
# arguments from command line
if args.force:
yield "--force"
if args.log_handler is not None:
yield from ["--log-handler", args.log_handler.value]
if args.quiet:
yield "--quiet"
if args.unsafe:
yield "--unsafe"

View File

@ -17,12 +17,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
import argparse
from types import TracebackType
from typing import Literal, Optional, Type
from typing import Literal, Self
from ahriman import version
from ahriman.core.configuration import Configuration
@ -88,6 +86,7 @@ class Lock(LazyLogging):
check if current user is actually owner of ahriman root
"""
check_user(self.paths, unsafe=self.unsafe)
self.paths.tree_create()
def clear(self) -> None:
"""
@ -111,15 +110,18 @@ class Lock(LazyLogging):
except FileExistsError:
raise DuplicateRunError()
def __enter__(self) -> Lock:
def __enter__(self) -> Self:
"""
default workflow is the following:
1. Check user UID
2. Check if there is lock file
3. Check web status watcher status
4. Create lock file
4. Create lock file and directory tree
5. Report to status page if enabled
Returns:
Self: always instance of self
"""
self.check_user()
self.check_version()
@ -127,14 +129,14 @@ class Lock(LazyLogging):
self.reporter.update_self(BuildStatusEnum.Building)
return self
def __exit__(self, exc_type: Optional[Type[Exception]], exc_val: Optional[Exception],
def __exit__(self, exc_type: type[Exception] | None, exc_val: Exception | None,
exc_tb: TracebackType) -> Literal[False]:
"""
remove lock file when done
Args:
exc_type(Optional[Type[Exception]]): exception type name if any
exc_val(Optional[Exception]): exception raised if any
exc_type(type[Exception] | None): exception type name if any
exc_val(Exception | None): exception raised if any
exc_tb(TracebackType): exception traceback if any
Returns:

View File

@ -17,8 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from collections.abc import Iterator
from contextvars import ContextVar
from typing import Any, Dict, Iterator, TypeVar
from typing import Any, TypeVar
from ahriman.models.context_key import ContextKey
@ -35,7 +36,7 @@ class _Context:
"""
default constructor. Must not be used directly
"""
self._content: Dict[str, Any] = {}
self._content: dict[str, Any] = {}
def get(self, key: ContextKey[T]) -> T:
"""

View File

@ -19,48 +19,45 @@
#
import shutil
from collections.abc import Callable, Generator
from functools import cached_property
from pathlib import Path
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
from typing import Any, Callable, Generator, Set
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore[import]
from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging
from ahriman.core.util import trim_package
from ahriman.models.pacman_synchronization import PacmanSynchronization
from ahriman.models.repository_paths import RepositoryPaths
class Pacman(LazyLogging):
"""
alpm wrapper
Attributes:
handle(Handle): pyalpm root ``Handle``
"""
handle: Handle
def __init__(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> None:
def __init__(self, architecture: str, configuration: Configuration, *,
refresh_database: PacmanSynchronization) -> None:
"""
default constructor
Args:
architecture(str): repository architecture
configuration(Configuration): configuration instance
refresh_database(int): synchronize local cache to remote. If set to ``0``, no synchronization will be
enabled, if set to ``1`` - normal synchronization, if set to ``2`` - force synchronization
refresh_database(PacmanSynchronization): synchronize local cache to remote
"""
self.__create_handle_fn: Callable[[], Handle] = lambda: self.__create_handle(
architecture, configuration, refresh_database=refresh_database)
def __create_handle(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> Handle:
def __create_handle(self, architecture: str, configuration: Configuration, *,
refresh_database: PacmanSynchronization) -> Handle:
"""
create lazy handle function
Args:
architecture(str): repository architecture
configuration(Configuration): configuration instance
refresh_database(int): synchronize local cache to remote. If set to ``0``, no synchronization will be
enabled, if set to ``1`` - normal synchronization, if set to ``2`` - force synchronization
refresh_database(PacmanSynchronization): synchronize local cache to remote
Returns:
Handle: fully initialized pacman handle
@ -78,10 +75,20 @@ class Pacman(LazyLogging):
self.database_copy(handle, database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
if use_ahriman_cache and refresh_database:
self.database_sync(handle, force=refresh_database > 1)
self.database_sync(handle, force=refresh_database == PacmanSynchronization.Force)
return handle
@cached_property
def handle(self) -> Handle:
"""
pyalpm handle
Returns:
Handle: generated pyalpm handle instance
"""
return self.__create_handle_fn()
def database_copy(self, handle: Handle, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
use_ahriman_cache: bool) -> None:
"""
@ -166,14 +173,14 @@ class Pacman(LazyLogging):
continue
yield package
def packages(self) -> Set[str]:
def packages(self) -> set[str]:
"""
get list of packages known for alpm
Returns:
Set[str]: list of package names
set[str]: list of package names
"""
result: Set[str] = set()
result: set[str] = set()
for database in self.handle.get_syncdbs():
for package in database.pkgcache:
# package itself
@ -182,22 +189,3 @@ class Pacman(LazyLogging):
result.update(trim_package(provides) for provides in package.provides)
return result
def __getattr__(self, item: str) -> Any:
"""
pacman handle extractor
Args:
item(str): property name
Returns:
Any: attribute by its name
Raises:
AttributeError: in case if no such attribute found
"""
if item == "handle":
handle = self.__create_handle_fn()
setattr(self, item, handle)
return handle
return super().__getattr__(item) # required for logging attribute

View File

@ -19,7 +19,7 @@
#
import requests
from typing import Any, Dict, List, Type
from typing import Any
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
@ -44,28 +44,8 @@ class AUR(Remote):
DEFAULT_RPC_VERSION = "5"
DEFAULT_TIMEOUT = 30
@staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
"""
parse RPC response to package list
Args:
response(Dict[str, Any]): RPC response json
Returns:
List[AURPackage]: list of parsed packages
Raises:
InvalidPackageInfo: for error API response
"""
response_type = response["type"]
if response_type == "error":
error_details = response.get("error", "Unknown API error")
raise PackageInfoError(error_details)
return [AURPackage.from_json(package) for package in response["results"]]
@classmethod
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str:
def remote_git_url(cls, package_base: str, repository: str) -> str:
"""
generate remote git url from the package base
@ -79,7 +59,7 @@ class AUR(Remote):
return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git"
@classmethod
def remote_web_url(cls: Type[Remote], package_base: str) -> str:
def remote_web_url(cls, package_base: str) -> str:
"""
generate remote web url from the package base
@ -91,7 +71,27 @@ class AUR(Remote):
"""
return f"{AUR.DEFAULT_AUR_URL}/packages/{package_base}"
def make_request(self, request_type: str, *args: str, **kwargs: str) -> List[AURPackage]:
@staticmethod
def parse_response(response: dict[str, Any]) -> list[AURPackage]:
"""
parse RPC response to package list
Args:
response(dict[str, Any]): RPC response json
Returns:
list[AURPackage]: list of parsed packages
Raises:
InvalidPackageInfo: for error API response
"""
response_type = response["type"]
if response_type == "error":
error_details = response.get("error", "Unknown API error")
raise PackageInfoError(error_details)
return [AURPackage.from_json(package) for package in response["results"]]
def make_request(self, request_type: str, *args: str, **kwargs: str) -> list[AURPackage]:
"""
perform request to AUR RPC
@ -101,9 +101,9 @@ class AUR(Remote):
**kwargs(str): list of additional named parameters like by
Returns:
List[AURPackage]: response parsed to package list
list[AURPackage]: response parsed to package list
"""
query: Dict[str, Any] = {
query: dict[str, Any] = {
"type": request_type,
"v": self.DEFAULT_RPC_VERSION
}
@ -115,7 +115,11 @@ class AUR(Remote):
query[key] = value
try:
response = requests.get(self.DEFAULT_RPC_URL, params=query, timeout=self.DEFAULT_TIMEOUT)
response = requests.get(
self.DEFAULT_RPC_URL,
params=query,
headers={"User-Agent": self.DEFAULT_USER_AGENT},
timeout=self.DEFAULT_TIMEOUT)
response.raise_for_status()
return self.parse_response(response.json())
except requests.HTTPError as e:
@ -145,7 +149,7 @@ class AUR(Remote):
except StopIteration:
raise UnknownPackageError(package_name)
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
def package_search(self, *keywords: str, pacman: Pacman) -> list[AURPackage]:
"""
search package in AUR web
@ -154,6 +158,6 @@ class AUR(Remote):
pacman(Pacman): alpm wrapper instance
Returns:
List[AURPackage]: list of packages which match the criteria
list[AURPackage]: list of packages which match the criteria
"""
return self.make_request("search", *keywords, by="name-desc")

View File

@ -19,7 +19,7 @@
#
import requests
from typing import Any, Dict, List, Type
from typing import Any
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
@ -34,36 +34,20 @@ class Official(Remote):
Attributes:
DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url
DEFAULT_SEARCH_REPOSITORIES(List[str]): (class attribute) default list of repositories to search
DEFAULT_ARCHLINUX_GIT_URL(str): (class attribute) default url for git packages
DEFAULT_SEARCH_REPOSITORIES(list[str]): (class attribute) default list of repositories to search
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
"""
DEFAULT_ARCHLINUX_GIT_URL = "https://gitlab.archlinux.org"
DEFAULT_ARCHLINUX_URL = "https://archlinux.org"
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib", "Community"]
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib"]
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
DEFAULT_TIMEOUT = 30
@staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
"""
parse RPC response to package list
Args:
response(Dict[str, Any]): RPC response json
Returns:
List[AURPackage]: list of parsed packages
Raises:
InvalidPackageInfo: for error API response
"""
if not response["valid"]:
raise PackageInfoError("API validation error")
return [AURPackage.from_repo(package) for package in response["results"]]
@classmethod
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str:
def remote_git_url(cls, package_base: str, repository: str) -> str:
"""
generate remote git url from the package base
@ -74,12 +58,10 @@ class Official(Remote):
Returns:
str: git url for the specific base
"""
if repository.lower() in ("core", "extra", "testing", "kde-unstable"):
return "https://github.com/archlinux/svntogit-packages.git" # hardcoded, ok
return "https://github.com/archlinux/svntogit-community.git"
return f"{Official.DEFAULT_ARCHLINUX_GIT_URL}/archlinux/packaging/packages/{package_base}.git"
@classmethod
def remote_web_url(cls: Type[Remote], package_base: str) -> str:
def remote_web_url(cls, package_base: str) -> str:
"""
generate remote web url from the package base
@ -91,7 +73,25 @@ class Official(Remote):
"""
return f"{Official.DEFAULT_ARCHLINUX_URL}/packages/{package_base}"
def make_request(self, *args: str, by: str) -> List[AURPackage]:
@staticmethod
def parse_response(response: dict[str, Any]) -> list[AURPackage]:
"""
parse RPC response to package list
Args:
response(dict[str, Any]): RPC response json
Returns:
list[AURPackage]: list of parsed packages
Raises:
InvalidPackageInfo: for error API response
"""
if not response["valid"]:
raise PackageInfoError("API validation error")
return [AURPackage.from_repo(package) for package in response["results"]]
def make_request(self, *args: str, by: str) -> list[AURPackage]:
"""
perform request to official repositories RPC
@ -100,12 +100,13 @@ class Official(Remote):
by(str): search by the field
Returns:
List[AURPackage]: response parsed to package list
list[AURPackage]: response parsed to package list
"""
try:
response = requests.get(
self.DEFAULT_RPC_URL,
params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES},
headers={"User-Agent": self.DEFAULT_USER_AGENT},
timeout=self.DEFAULT_TIMEOUT)
response.raise_for_status()
return self.parse_response(response.json())
@ -133,7 +134,7 @@ class Official(Remote):
except StopIteration:
raise UnknownPackageError(package_name)
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
def package_search(self, *keywords: str, pacman: Pacman) -> list[AURPackage]:
"""
search package in AUR web
@ -142,6 +143,6 @@ class Official(Remote):
pacman(Pacman): alpm wrapper instance
Returns:
List[AURPackage]: list of packages which match the criteria
list[AURPackage]: list of packages which match the criteria
"""
return self.make_request(*keywords, by="q")

View File

@ -17,10 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
from typing import Dict, List, Type
from ahriman import version
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.log import LazyLogging
from ahriman.models.aur_package import AURPackage
@ -30,6 +27,9 @@ class Remote(LazyLogging):
"""
base class for remote package search
Attributes:
DEFAULT_USER_AGENT(str): (class attribute) default user agent
Examples:
These classes are designed to be used without instancing. In order to achieve it several class methods are
provided: ``info``, ``multisearch`` and ``search``. Thus, the basic flow is the following::
@ -43,8 +43,10 @@ class Remote(LazyLogging):
directly, whereas ``multisearch`` splits search one by one and finds intersection between search results.
"""
DEFAULT_USER_AGENT = f"ahriman/{version.__version__}"
@classmethod
def info(cls: Type[Remote], package_name: str, *, pacman: Pacman) -> AURPackage:
def info(cls, package_name: str, *, pacman: Pacman) -> AURPackage:
"""
get package info by its name
@ -58,7 +60,7 @@ class Remote(LazyLogging):
return cls().package_info(package_name, pacman=pacman)
@classmethod
def multisearch(cls: Type[Remote], *keywords: str, pacman: Pacman) -> List[AURPackage]:
def multisearch(cls, *keywords: str, pacman: Pacman) -> list[AURPackage]:
"""
search in remote repository by using API with multiple words. This method is required in order to handle
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
@ -68,10 +70,10 @@ class Remote(LazyLogging):
pacman(Pacman): alpm wrapper instance
Returns:
List[AURPackage]: list of packages each of them matches all search terms
list[AURPackage]: list of packages each of them matches all search terms
"""
instance = cls()
packages: Dict[str, AURPackage] = {}
packages: dict[str, AURPackage] = {}
for term in filter(lambda word: len(word) >= 3, keywords):
portion = instance.search(term, pacman=pacman)
packages = {
@ -82,7 +84,7 @@ class Remote(LazyLogging):
return list(packages.values())
@classmethod
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str:
def remote_git_url(cls, package_base: str, repository: str) -> str:
"""
generate remote git url from the package base
@ -99,7 +101,7 @@ class Remote(LazyLogging):
raise NotImplementedError
@classmethod
def remote_web_url(cls: Type[Remote], package_base: str) -> str:
def remote_web_url(cls, package_base: str) -> str:
"""
generate remote web url from the package base
@ -115,7 +117,7 @@ class Remote(LazyLogging):
raise NotImplementedError
@classmethod
def search(cls: Type[Remote], *keywords: str, pacman: Pacman) -> List[AURPackage]:
def search(cls, *keywords: str, pacman: Pacman) -> list[AURPackage]:
"""
search package in AUR web
@ -124,7 +126,7 @@ class Remote(LazyLogging):
pacman(Pacman): alpm wrapper instance
Returns:
List[AURPackage]: list of packages which match the criteria
list[AURPackage]: list of packages which match the criteria
"""
return cls().package_search(*keywords, pacman=pacman)
@ -144,7 +146,7 @@ class Remote(LazyLogging):
"""
raise NotImplementedError
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
def package_search(self, *keywords: str, pacman: Pacman) -> list[AURPackage]:
"""
search package in AUR web
@ -153,7 +155,7 @@ class Remote(LazyLogging):
pacman(Pacman): alpm wrapper instance
Returns:
List[AURPackage]: list of packages which match the criteria
list[AURPackage]: list of packages which match the criteria
Raises:
NotImplementedError: not implemented method

View File

@ -18,7 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pathlib import Path
from typing import List
from ahriman.core.exceptions import BuildError
from ahriman.core.log import LazyLogging
@ -33,20 +32,20 @@ class Repo(LazyLogging):
Attributes:
name(str): repository name
paths(RepositoryPaths): repository paths instance
sign_args(List[str]): additional args which have to be used to sign repository archive
sign_args(list[str]): additional args which have to be used to sign repository archive
uid(int): uid of the repository owner user
"""
_check_output = check_output
def __init__(self, name: str, paths: RepositoryPaths, sign_args: List[str]) -> None:
def __init__(self, name: str, paths: RepositoryPaths, sign_args: list[str]) -> None:
"""
default constructor
Args:
name(str): repository name
paths(RepositoryPaths): repository paths instance
sign_args(List[str]): additional args which have to be used to sign repository archive
sign_args(list[str]): additional args which have to be used to sign repository archive
"""
self.name = name
self.paths = paths

View File

@ -19,8 +19,6 @@
#
from __future__ import annotations
from typing import Optional, Type
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.log import LazyLogging
@ -64,8 +62,8 @@ class Auth(LazyLogging):
"""
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#login-modal" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>"""
@classmethod
def load(cls: Type[Auth], configuration: Configuration, database: SQLite) -> Auth:
@staticmethod
def load(configuration: Configuration, database: SQLite) -> Auth:
"""
load authorization module from settings
@ -83,15 +81,15 @@ class Auth(LazyLogging):
if provider == AuthSettings.OAuth:
from ahriman.core.auth.oauth import OAuth
return OAuth(configuration, database)
return cls(configuration)
return Auth(configuration)
async def check_credentials(self, username: Optional[str], password: Optional[str]) -> bool:
async def check_credentials(self, username: str | None, password: str | None) -> bool:
"""
validate user password
Args:
username(Optional[str]): username
password(Optional[str]): entered password
username(str | None): username
password(str | None): entered password
Returns:
bool: True in case if password matches, False otherwise
@ -99,12 +97,12 @@ class Auth(LazyLogging):
del username, password
return True
async def known_username(self, username: Optional[str]) -> bool:
async def known_username(self, username: str | None) -> bool:
"""
check if user is known
Args:
username(Optional[str]): username
username(str | None): username
Returns:
bool: True in case if user is known and can be authorized and False otherwise
@ -112,14 +110,14 @@ class Auth(LazyLogging):
del username
return True
async def verify_access(self, username: str, required: UserAccess, context: Optional[str]) -> bool:
async def verify_access(self, username: str, required: UserAccess, context: str | None) -> bool:
"""
validate if user has access to requested resource
Args:
username(str): username
required(UserAccess): required access level
context(Optional[str]): URI request path
context(str | None): URI request path
Returns:
bool: True in case if user is allowed to do this request and False otherwise

View File

@ -20,7 +20,7 @@
from typing import Any
try:
import aiohttp_security # type: ignore
import aiohttp_security # type: ignore[import]
_has_aiohttp_security = True
except ImportError:
_has_aiohttp_security = False

View File

@ -17,8 +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/>.
#
from typing import Optional
from ahriman.core.auth import Auth
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
@ -50,13 +48,13 @@ class Mapping(Auth):
self.database = database
self.salt = configuration.get("auth", "salt")
async def check_credentials(self, username: Optional[str], password: Optional[str]) -> bool:
async def check_credentials(self, username: str | None, password: str | None) -> bool:
"""
validate user password
Args:
username(Optional[str]): username
password(Optional[str]): entered password
username(str | None): username
password(str | None): entered password
Returns:
bool: True in case if password matches, False otherwise
@ -66,7 +64,7 @@ class Mapping(Auth):
user = self.get_user(username)
return user is not None and user.check_credentials(password, self.salt)
def get_user(self, username: str) -> Optional[User]:
def get_user(self, username: str) -> User | None:
"""
retrieve user from in-memory mapping
@ -74,30 +72,30 @@ class Mapping(Auth):
username(str): username
Returns:
Optional[User]: user descriptor if username is known and None otherwise
User | None: user descriptor if username is known and None otherwise
"""
return self.database.user_get(username)
async def known_username(self, username: Optional[str]) -> bool:
async def known_username(self, username: str | None) -> bool:
"""
check if user is known
Args:
username(Optional[str]): username
username(str | None): username
Returns:
bool: True in case if user is known and can be authorized and False otherwise
"""
return username is not None and self.get_user(username) is not None
async def verify_access(self, username: str, required: UserAccess, context: Optional[str]) -> bool:
async def verify_access(self, username: str, required: UserAccess, context: str | None) -> bool:
"""
validate if user has access to requested resource
Args:
username(str): username
required(UserAccess): required access level
context(Optional[str]): URI request path
context(str | None): URI request path
Returns:
bool: True in case if user is allowed to do this request and False otherwise

View File

@ -19,8 +19,6 @@
#
import aioauth_client
from typing import Optional, Type
from ahriman.core.auth.mapping import Mapping
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
@ -72,7 +70,7 @@ class OAuth(Mapping):
return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-google"></i> login</a>"""
@staticmethod
def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]:
def get_provider(name: str) -> type[aioauth_client.OAuth2Client]:
"""
load OAuth2 provider by name
@ -80,12 +78,12 @@ class OAuth(Mapping):
name(str): name of the provider. Must be valid class defined in aioauth-client library
Returns:
Type[aioauth_client.OAuth2Client]: loaded provider type
type[aioauth_client.OAuth2Client]: loaded provider type
Raises:
InvalidOption: in case if invalid OAuth provider name supplied
"""
provider: Type[aioauth_client.OAuth2Client] = getattr(aioauth_client, name)
provider: type[aioauth_client.OAuth2Client] = getattr(aioauth_client, name)
try:
is_oauth2_client = issubclass(provider, aioauth_client.OAuth2Client)
except TypeError: # what if it is random string?
@ -114,7 +112,7 @@ class OAuth(Mapping):
uri: str = client.get_authorize_url(scope=self.scopes, redirect_uri=self.redirect_uri)
return uri
async def get_oauth_username(self, code: str) -> Optional[str]:
async def get_oauth_username(self, code: str) -> str | None:
"""
extract OAuth username from remote
@ -122,7 +120,7 @@ class OAuth(Mapping):
code(str): authorization code provided by external service
Returns:
Optional[str]: username as is in OAuth provider
str | None: username as is in OAuth provider
"""
try:
client = self.get_client()
@ -130,7 +128,7 @@ class OAuth(Mapping):
client.access_token = access_token
user, _ = await client.user_info()
username: str = user.email # type: ignore
username: str = user.email # type: ignore[attr-defined]
return username
except Exception:
self.logger.exception("got exception while performing request")

View File

@ -20,7 +20,6 @@
import shutil
from pathlib import Path
from typing import List, Optional
from ahriman.core.log import LazyLogging
from ahriman.core.util import check_output, utcnow, walk
@ -44,7 +43,7 @@ class Sources(LazyLogging):
_check_output = check_output
@staticmethod
def extend_architectures(sources_dir: Path, architecture: str) -> List[PkgbuildPatch]:
def extend_architectures(sources_dir: Path, architecture: str) -> list[PkgbuildPatch]:
"""
extend existing PKGBUILD with repository architecture
@ -53,7 +52,7 @@ class Sources(LazyLogging):
architecture(str): repository architecture
Returns:
List[PkgbuildPatch]: generated patch for PKGBUILD architectures if required
list[PkgbuildPatch]: generated patch for PKGBUILD architectures if required
"""
architectures = Package.supported_architectures(sources_dir)
if "any" in architectures: # makepkg does not like when there is any other arch except for any
@ -62,13 +61,13 @@ class Sources(LazyLogging):
return [PkgbuildPatch("arch", list(architectures))]
@staticmethod
def fetch(sources_dir: Path, remote: Optional[RemoteSource]) -> None:
def fetch(sources_dir: Path, remote: RemoteSource | None) -> None:
"""
either clone repository or update it to origin/``remote.branch``
Args:
sources_dir(Path): local path to fetch
remote(Optional[RemoteSource]): remote target (from where to fetch)
remote(RemoteSource | None): remote target (from where to fetch)
"""
instance = Sources()
# local directory exists and there is .git directory
@ -126,15 +125,21 @@ class Sources(LazyLogging):
Sources._check_output("git", "init", "--initial-branch", instance.DEFAULT_BRANCH,
cwd=sources_dir, logger=instance.logger)
# extract local files...
files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Package.local_files(sources_dir)]
instance.add(sources_dir, *files)
# ...and commit them
instance.commit(sources_dir, author="ahriman <ahriman@localhost>")
@staticmethod
def load(sources_dir: Path, package: Package, patches: List[PkgbuildPatch], paths: RepositoryPaths) -> None:
def load(sources_dir: Path, package: Package, patches: list[PkgbuildPatch], paths: RepositoryPaths) -> None:
"""
fetch sources from remote and apply patches
Args:
sources_dir(Path): local path to fetch
package(Package): package definitions
patches(List[PkgbuildPatch]): optional patch to be applied
patches(list[PkgbuildPatch]): optional patch to be applied
paths(RepositoryPaths): repository paths instance
"""
instance = Sources()
@ -165,7 +170,7 @@ class Sources(LazyLogging):
return f"{diff}\n" # otherwise, patch will be broken
@staticmethod
def push(sources_dir: Path, remote: RemoteSource, *pattern: str, commit_author: Optional[str] = None) -> None:
def push(sources_dir: Path, remote: RemoteSource, *pattern: str, commit_author: str | None = None) -> None:
"""
commit selected changes and push files to the remote repository
@ -173,7 +178,7 @@ class Sources(LazyLogging):
sources_dir(Path): local path to git repository
remote(RemoteSource): remote target, branch and url
*pattern(str): glob patterns
commit_author(Optional[str], optional): commit author in form of git config (i.e. ``user <user@host>``)
commit_author(str | None, optional): commit author in form of git config (i.e. ``user <user@host>``)
(Default value = None)
"""
instance = Sources()
@ -192,7 +197,7 @@ class Sources(LazyLogging):
--intent-to-add git flag (Default value = False)
"""
# glob directory to find files which match the specified patterns
found_files: List[Path] = []
found_files: list[Path] = []
for glob in pattern:
found_files.extend(sources_dir.glob(glob))
if not found_files:
@ -203,15 +208,15 @@ class Sources(LazyLogging):
Sources._check_output("git", "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files],
cwd=sources_dir, logger=self.logger)
def commit(self, sources_dir: Path, message: Optional[str] = None, author: Optional[str] = None) -> None:
def commit(self, sources_dir: Path, message: str | None = None, author: str | None = None) -> None:
"""
commit changes
Args:
sources_dir(Path): local path to git repository
message(Optional[str], optional): optional commit message if any. If none set, message will be generated
message(str | None, optional): optional commit message if any. If none set, message will be generated
according to the current timestamp (Default value = None)
author(Optional[str], optional): optional commit author if any (Default value = None)
author(str | None, optional): optional commit author if any (Default value = None)
"""
if message is None:
message = f"Autogenerated commit at {utcnow()}"

View File

@ -18,7 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pathlib import Path
from typing import List
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
@ -60,15 +59,16 @@ class Task(LazyLogging):
self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[])
self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[])
def build(self, sources_dir: Path) -> List[Path]:
def build(self, sources_dir: Path, packager: str | None = None) -> list[Path]:
"""
run package build
Args:
sources_dir(Path): path to where sources are
packager(str | None, optional): optional packager override (Default value = None)
Returns:
List[Path]: paths of produced packages
list[Path]: paths of produced packages
"""
command = [self.build_command, "-r", str(self.paths.chroot)]
command.extend(self.archbuild_flags)
@ -76,12 +76,18 @@ class Task(LazyLogging):
command.extend(["--"] + self.makepkg_flags)
self.logger.info("using %s for %s", command, self.package.base)
environment: dict[str, str] = {}
if packager is not None:
environment["PACKAGER"] = packager
self.logger.info("using environment variables %s", environment)
Task._check_output(
*command,
exception=BuildError(self.package.base),
cwd=sources_dir,
logger=self.logger,
user=self.uid)
user=self.uid,
environment=environment)
# well it is not actually correct, but we can deal with it
packages = Task._check_output(

View File

@ -17,14 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
import configparser
import shlex
import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Tuple, Type
from typing import Any, Self
from ahriman.core.exceptions import InitializeError
from ahriman.models.repository_paths import RepositoryPaths
@ -35,11 +34,12 @@ class Configuration(configparser.RawConfigParser):
extension for built-in configuration parser
Attributes:
ARCHITECTURE_SPECIFIC_SECTIONS(List[str]): (class attribute) known sections which can be architecture specific.
ARCHITECTURE_SPECIFIC_SECTIONS(list[str]): (class attribute) known sections which can be architecture specific.
Required by dump and merging functions
SYSTEM_CONFIGURATION_PATH(Path): (class attribute) default system configuration path distributed by package
architecture(Optional[str]): repository architecture
path(Optional[Path]): path to root configuration file
architecture(str | None): repository architecture
includes(list[Path]): list of includes which were read
path(Path | None): path to root configuration file
Examples:
Configuration class provides additional method in order to handle application configuration. Since this class is
@ -63,7 +63,7 @@ class Configuration(configparser.RawConfigParser):
ARCHITECTURE_SPECIFIC_SECTIONS = ["alpm", "build", "sign", "web"]
SYSTEM_CONFIGURATION_PATH = Path(sys.prefix) / "share" / "ahriman" / "settings" / "ahriman.ini"
converters: Dict[str, Callable[[str], Any]] # typing guard
converters: dict[str, Callable[[str], Any]] # typing guard
def __init__(self, allow_no_value: bool = False) -> None:
"""
@ -77,8 +77,9 @@ class Configuration(configparser.RawConfigParser):
"list": shlex.split,
"path": self._convert_path,
})
self.architecture: Optional[str] = None
self.path: Optional[Path] = None
self.architecture: str | None = None
self.path: Path | None = None
self.includes: list[Path] = []
@property
def include(self) -> Path:
@ -100,6 +101,16 @@ class Configuration(configparser.RawConfigParser):
"""
return self.getpath("settings", "logging")
@property
def repository_name(self) -> str:
"""
repository name as defined by configuration
Returns:
str: repository name from configuration
"""
return self.get("repository", "name")
@property
def repository_paths(self) -> RepositoryPaths:
"""
@ -112,7 +123,7 @@ class Configuration(configparser.RawConfigParser):
return RepositoryPaths(self.getpath("repository", "root"), architecture)
@classmethod
def from_path(cls: Type[Configuration], path: Path, architecture: str) -> Configuration:
def from_path(cls, path: Path, architecture: str) -> Self:
"""
constructor with full object initialization
@ -121,7 +132,7 @@ class Configuration(configparser.RawConfigParser):
architecture(str): repository architecture
Returns:
Configuration: configuration instance
Self: configuration instance
"""
configuration = cls()
configuration.load(path)
@ -157,12 +168,12 @@ class Configuration(configparser.RawConfigParser):
return path
return self.path.parent / path
def check_loaded(self) -> Tuple[Path, str]:
def check_loaded(self) -> tuple[Path, str]:
"""
check if service was actually loaded
Returns:
Tuple[Path, str]: configuration root path and architecture if loaded
tuple[Path, str]: configuration root path and architecture if loaded
Raises:
InitializeError: in case if architecture and/or path are not set
@ -171,12 +182,12 @@ class Configuration(configparser.RawConfigParser):
raise InitializeError("Configuration path and/or architecture are not set")
return self.path, self.architecture
def dump(self) -> Dict[str, Dict[str, str]]:
def dump(self) -> dict[str, dict[str, str]]:
"""
dump configuration to dictionary
Returns:
Dict[str, Dict[str, str]]: configuration dump for specific architecture
dict[str, dict[str, str]]: configuration dump for specific architecture
"""
return {
section: dict(self[section])
@ -184,12 +195,12 @@ class Configuration(configparser.RawConfigParser):
}
# pylint and mypy are too stupid to find these methods
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ... # type: ignore
# pylint: disable=missing-function-docstring,unused-argument
def getlist(self, *args: Any, **kwargs: Any) -> list[str]: ... # type: ignore[empty-body]
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore[empty-body]
def gettype(self, section: str, architecture: str, *, fallback: Optional[str] = None) -> Tuple[str, str]:
def gettype(self, section: str, architecture: str, *, fallback: str | None = None) -> tuple[str, str]:
"""
get type variable with fallback to old logic. Despite the fact that it has same semantics as other get* methods,
but it has different argument list
@ -197,11 +208,11 @@ class Configuration(configparser.RawConfigParser):
Args:
section(str): section name
architecture(str): repository architecture
fallback(Optional[str], optional): optional fallback type if any. If set, second element of the tuple will
fallback(str | None, optional): optional fallback type if any. If set, second element of the tuple will
be always set to this value (Default value = None)
Returns:
Tuple[str, str]: section name and found type name
tuple[str, str]: section name and found type name
Raises:
configparser.NoSectionError: in case if no section found
@ -213,9 +224,9 @@ class Configuration(configparser.RawConfigParser):
if self.has_section(full_section):
return full_section, section
# okay lets just use section as type
if not self.has_section(section):
raise configparser.NoSectionError(section)
return section, section
if self.has_section(section):
return section, section
raise configparser.NoSectionError(section)
def load(self, path: Path) -> None:
"""
@ -234,11 +245,13 @@ class Configuration(configparser.RawConfigParser):
"""
load configuration includes
"""
self.includes = [] # reset state
try:
for path in sorted(self.include.glob("*.ini")):
if path == self.logging_path:
continue # we don't want to load logging explicitly
self.read(path)
self.includes.append(path)
except (FileNotFoundError, configparser.NoOptionError, configparser.NoSectionError):
pass

View File

@ -17,13 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from typing import Any, Dict
from typing import Any
__all__ = ["CONFIGURATION_SCHEMA", "ConfigurationSchema"]
ConfigurationSchema = Dict[str, Dict[str, Any]]
ConfigurationSchema = dict[str, dict[str, Any]]
CONFIGURATION_SCHEMA: ConfigurationSchema = {
@ -163,6 +163,11 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"coerce": "list",
"schema": {"type": "string"},
},
"triggers_known": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
"vcs_allowed_age": {
"type": "integer",
"coerce": "integer",
@ -186,10 +191,6 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"sign": {
"type": "dict",
"allow_unknown": True,
"keysrules": {
"type": "string",
"anyof_regex": ["^target$", "^key$", "^key_.*"],
},
"schema": {
"target": {
"type": "list",

View File

@ -19,15 +19,15 @@
#
import ipaddress
from cerberus import TypeDefinition, Validator as RootValidator # type: ignore
from cerberus import TypeDefinition, Validator as RootValidator # type: ignore[import]
from pathlib import Path
from typing import Any, List
from typing import Any
from urllib.parse import urlparse
from ahriman.core.configuration import Configuration
class Validator(RootValidator): # type: ignore
class Validator(RootValidator):
"""
class which defines custom validation methods for the service configuration
@ -74,7 +74,7 @@ class Validator(RootValidator): # type: ignore
bool: value converted to boolean according to configuration rules
"""
# pylint: disable=protected-access
converted: bool = self.configuration._convert_to_boolean(value) # type: ignore
converted: bool = self.configuration._convert_to_boolean(value) # type: ignore[attr-defined]
return converted
def _normalize_coerce_integer(self, value: str) -> int:
@ -90,7 +90,7 @@ class Validator(RootValidator): # type: ignore
del self
return int(value)
def _normalize_coerce_list(self, value: str) -> List[str]:
def _normalize_coerce_list(self, value: str) -> list[str]:
"""
extract string list from string value
@ -98,17 +98,17 @@ class Validator(RootValidator): # type: ignore
value(str): converting value
Returns:
List[str]: value converted to string list instance according to configuration rules
list[str]: value converted to string list instance according to configuration rules
"""
converted: List[str] = self.configuration.converters["list"](value)
converted: list[str] = self.configuration.converters["list"](value)
return converted
def _validate_is_ip_address(self, constraint: List[str], field: str, value: str) -> None:
def _validate_is_ip_address(self, constraint: list[str], field: str, value: str) -> None:
"""
check if the specified value is valid ip address
Args:
constraint(List[str]): optional list of allowed special words (e.g. ``localhost``)
constraint(list[str]): optional list of allowed special words (e.g. ``localhost``)
field(str): field name to be checked
value(Path): value to be checked
@ -123,12 +123,12 @@ class Validator(RootValidator): # type: ignore
except ValueError:
self._error(field, f"Value {value} must be valid IP address")
def _validate_is_url(self, constraint: List[str], field: str, value: str) -> None:
def _validate_is_url(self, constraint: list[str], field: str, value: str) -> None:
"""
check if the specified value is a valid url
Args:
constraint(List[str]): optional list of supported schemas. If empty, no schema validation will be performed
constraint(list[str]): optional list of supported schemas. If empty, no schema validation will be performed
field(str): field name to be checked
value(str): value to be checked
@ -144,6 +144,24 @@ class Validator(RootValidator): # type: ignore
if constraint and url.scheme not in constraint:
self._error(field, f"Url {value} scheme must be one of {constraint}")
def _validate_path_is_absolute(self, constraint: bool, field: str, value: Path) -> None:
"""
check if path is absolute or not
Args:
constraint(bool): True in case if path must be absolute and False if it must be relative
field(str): field name to be checked
value(Path): value to be checked
Examples:
The rule's arguments are validated against this schema:
{"type": "boolean"}
"""
if constraint and not value.is_absolute():
self._error(field, f"Path {value} must be absolute")
if not constraint and value.is_absolute():
self._error(field, f"Path {value} must be relative")
def _validate_path_exists(self, constraint: bool, field: str, value: Path) -> None:
"""
check if paths exists
@ -159,3 +177,5 @@ class Validator(RootValidator): # type: ignore
"""
if constraint and not value.exists():
self._error(field, f"Path {value} must exist")
if not constraint and value.exists():
self._error(field, f"Path {value} must not exist")

View File

@ -17,11 +17,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from collections.abc import Callable
from importlib import import_module
from pathlib import Path
from pkgutil import iter_modules
from sqlite3 import Connection, Cursor
from typing import Callable, List
from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging
@ -83,22 +83,22 @@ class Migrations(LazyLogging):
"data migration %s at index %s has been performed",
migration.name, migration.index)
def migrations(self) -> List[Migration]:
def migrations(self) -> list[Migration]:
"""
extract all migrations from the current package
idea comes from https://julienharbulot.com/python-dynamical-import.html
Returns:
List[Migration]: list of found migrations
list[Migration]: list of found migrations
"""
migrations: List[Migration] = []
migrations: list[Migration] = []
package_dir = Path(__file__).resolve().parent
modules = [module_name for (_, module_name, _) in iter_modules([str(package_dir)])]
for index, module_name in enumerate(sorted(modules)):
module = import_module(f"{__name__}.{module_name}")
steps: List[str] = getattr(module, "steps", [])
steps: list[str] = getattr(module, "steps", [])
self.logger.debug("found migration %s at index %s with steps count %s", module_name, index, len(steps))
migrate_data: Callable[[Connection, Configuration], None] = \

View File

@ -23,6 +23,7 @@ from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
from ahriman.core.util import package_like
from ahriman.models.package import Package
from ahriman.models.pacman_synchronization import PacmanSynchronization
__all__ = ["migrate_data", "steps"]
@ -61,7 +62,7 @@ def migrate_package_depends(connection: Connection, configuration: Configuration
return
_, architecture = configuration.check_loaded()
pacman = Pacman(architecture, configuration, refresh_database=False)
pacman = Pacman(architecture, configuration, refresh_database=PacmanSynchronization.Disabled)
package_list = []
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):

View File

@ -0,0 +1,80 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from sqlite3 import Connection
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
from ahriman.core.util import package_like
from ahriman.models.package import Package
from ahriman.models.pacman_synchronization import PacmanSynchronization
__all__ = ["migrate_data", "steps"]
steps = [
"""
alter table packages add column check_depends json
""",
]
def migrate_data(connection: Connection, configuration: Configuration) -> None:
"""
perform data migration
Args:
connection(Connection): database connection
configuration(Configuration): configuration instance
"""
migrate_package_check_depends(connection, configuration)
def migrate_package_check_depends(connection: Connection, configuration: Configuration) -> None:
"""
migrate package check depends fields
Args:
connection(Connection): database connection
configuration(Configuration): configuration instance
"""
if not configuration.repository_paths.repository.is_dir():
return
_, architecture = configuration.check_loaded()
pacman = Pacman(architecture, configuration, refresh_database=PacmanSynchronization.Disabled)
package_list = []
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
base = Package.from_archive(full_path, pacman, remote=None)
for package, description in base.packages.items():
package_list.append({
"check_depends": description.check_depends,
"package": package,
})
connection.executemany(
"""
update packages set
check_depends = :check_depends
where package = :package
""",
package_list
)

View File

@ -0,0 +1,85 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from sqlite3 import Connection
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
from ahriman.core.util import package_like
from ahriman.models.package import Package
from ahriman.models.pacman_synchronization import PacmanSynchronization
__all__ = ["migrate_data", "steps"]
steps = [
"""
alter table users add column packager_id
""",
"""
alter table users add column key_id
""",
"""
alter table package_bases add column packager
""",
]
def migrate_data(connection: Connection, configuration: Configuration) -> None:
"""
perform data migration
Args:
connection(Connection): database connection
configuration(Configuration): configuration instance
"""
migrate_package_base_packager(connection, configuration)
def migrate_package_base_packager(connection: Connection, configuration: Configuration) -> None:
"""
migrate package packager field
Args:
connection(Connection): database connection
configuration(Configuration): configuration instance
"""
if not configuration.repository_paths.repository.is_dir():
return
_, architecture = configuration.check_loaded()
pacman = Pacman(architecture, configuration, refresh_database=PacmanSynchronization.Disabled)
package_list = []
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
package = Package.from_archive(full_path, pacman, remote=None)
package_list.append({
"package_base": package.base,
"packager": package.packager,
})
connection.executemany(
"""
update package_bases set
packager = :packager
where package_base = :package_base
""",
package_list
)

Some files were not shown because too many files have changed in this diff Show More