Compare commits

...

147 Commits

Author SHA1 Message Date
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
ec0550a275 Release 2.7.1 2023-03-06 01:15:47 +02:00
df23be9269 gracefully terminate web server
In previous revisions server was terminated by itself, thus no lock or
socket was removed. In new version, graceful termination of the queue
has been added as well as server now handles singals
2023-03-06 01:13:41 +02:00
a8c40a6b87 replace InitializeException with InitializeError in docs 2023-03-02 11:07:59 +02:00
a274f91677 simplify login ttl processing 2023-02-24 16:52:55 +02:00
13faf66bdb add more validation rules 2023-02-23 15:18:56 +02:00
4fb9335df9 add ability to read cookie secret from config 2023-02-22 18:47:56 +02:00
d517d8bfbb Release 2.7.0 2023-02-20 03:05:08 +02:00
37e57c13c8 update dependencies before build (#91)
Old implementation has used add step in order to fetch dependencies,
which could lead to build errors in case if dependency list was updated.

New solution uses dependencies which are declared at current version and
fetch them (if required and if enabled) before update process.

Closes #90
2023-02-12 06:02:30 +03:00
19bb19e9f5 handle .gitignore file correctly in remote push trigger 2023-02-11 04:41:24 +02:00
3a4e8f4d97 mask mypy warning
The newest mypy produces the following warning:

src/ahriman/application/handlers/search.py:43: error: Non-overlapping identity check (left operand type: "Union[_DefaultFactory[Any], Literal[_MISSING_TYPE.MISSING]]", right operand type: "Type[List[Any]]")  [comparison-overlap]

which is more likely caused by updated dataclass models to protoocol (however decorators are still calllable). This commit masks problematic line from checking
2023-02-09 22:46:08 +02:00
4db8ad8e8d hide passwords and secrets from repo-config subcommand by default 2023-02-05 16:44:48 +02:00
117f096d41 note about local database update (see #85) 2023-01-31 14:47:58 +02:00
917ec48be5 handle architecture specific fields for dependencies
This change requires srcinfo at least 0.1.2 version. Unfortunatelly aur
api don't support architecture specific arrays for now, so we just leave
it as is

Closes #82
2023-01-31 14:34:09 +02:00
0a2ba4ae07 filter empty packages from database
In some cases (e.g. during addition of the package to build queue) we don't have
full information about package inself; in these cases we produce lines
with empty architecture, which duplicates normal ones.

This commit changes architecture column type to required and also
filters packages which don't have architecture set yet.

Closes #83
2023-01-30 17:57:13 +02:00
9d7f63e549 trim version from provides list
Closes #87
2023-01-30 17:27:34 +02:00
25eee9ca5e add ability to suppress http logging errors (#86) 2023-01-30 17:19:01 +02:00
5af84955ac calculate dependencies based on package information (#89) 2023-01-30 17:28:05 +03:00
d3ad4c3c08 remove debug line 2023-01-27 16:41:41 +02:00
5bbb9d269b Release 2.6.1 2023-01-25 15:28:27 +02:00
17466d8d37 make oauth client trully optional (#84)
Same old song, after migraiton to packages some optional modules are
being imported globally which lead to making hard dependency
2023-01-25 15:25:42 +02:00
9e4e3b701b enable lock for web service 2023-01-18 01:39:55 +02:00
c6555cf2c7 Release 2.6.0 2023-01-16 01:35:12 +02:00
85baad6a41 add notes about non-x86_64 architecture setup 2023-01-15 18:16:29 +02:00
616a1950ae add ability to override pacman mirror in devtools configuration
This commit also extends configuration of the multilib option, adding
the ability to exlcude multilib repository from repositories list

Note, that in order to support repository list and mirror correctly,
alpm configuration section is now architectture specific
2023-01-15 18:05:15 +02:00
30b9bcb45a add note about other authroization options to gitremote triggers 2023-01-13 17:27:19 +02:00
d7356926c4 consider vcs flag and vcs_allowed_age during local packages update 2023-01-12 15:37:05 +02:00
43a7d09cab add ability to check for service updates 2023-01-12 15:37:05 +02:00
77954b988b subcommand review
Some commands have been moved to another group and thus having another
default name (old subcommands are still available...for now):

* daemon -> repo-daemon
* key-import -> service-key-import
* repo-clean -> service-clean
* repo-config -> service-config
* repo-config-validate -> service-config-validate
* repo-setup -> service-setup
* repo-shell -> service-shell
* version -> help-version
2023-01-12 15:37:05 +02:00
0239fb50b6 expose trigger configuration schema
Note that this commit contains the following breaking changes:

* remote pull and remote push triggers are now enabled by default (with
  empty target list)
* remote pull and remote push triggers now require target option to be
  set (old behaviour had fallback on `gitremote`)
* validation is now considered to be stable, so it is enabled by default
  in docker image (can be disabled however)
2023-01-10 03:33:52 +02:00
d942a70272 add config validator subcommand (#80)
* add config validator subcommand

* add --exit-code flag

* docs & faq update
2023-01-09 18:22:29 +03:00
1f07a89316 sort unsafe commands response 2023-01-04 18:00:10 +02:00
014007ade3 review unsafe commands access
Some commands were made unsafe in old versions, but nowadays they can be
run without having special privileges.

There was also a bug in which status commands were not available if you
are not ahriman user and unix socket is used. It has been fixed by
switching to manual socket creation (see also
https://github.com/aio-libs/aiohttp/issues/4155)
2023-01-04 17:45:44 +02:00
730f3ca0c9 copyright update 2023-01-04 03:43:10 +02:00
42c13b5d4b Release 2.5.4 2023-01-03 01:59:25 +02:00
04e5a263b7 add notes about documentation and methods inside class
Because I always forget which way I used before
2023-01-03 01:53:10 +02:00
caca1576c8 Correct way to allow setting context with existing
This reverts commit 5c4d3eeffd.

Original solution has introduced special workaround (strict flag) which
contradicts the concept of immutable context. Moreover, it introduces
possible side-effects, because child process will use the one set by
parent instead of having own one.

The correct solution is to re-create context in process entry point

Sorry, it was Jan 1 and I was drunk :(
2023-01-03 00:48:14 +02:00
98f2f19d5b Release 2.5.3 2023-01-02 03:24:11 +02:00
5c4d3eeffd allow setting context with existing
In case of running command from web interface, it will raise exception
because context has been copied with subprocesses
2023-01-02 03:21:15 +02:00
84d4523e85 Release 2.5.2 2023-01-02 01:57:09 +02:00
2c2eae2334 remote all gitfiles in git remote trigger
In case if there is .gitignore file with asterics, the pkgbuild upload
would not appear
2023-01-02 01:45:50 +02:00
214d6d7fdd Release 2.5.1 2022-12-31 14:58:37 +02:00
e9512e9a6a remote log for calculate version as it cleans logs 2022-12-31 14:48:21 +02:00
f984ea75d0 fully lazy handle load
In case of immediate handle load it would try to sync databases (or at
least to create database files), which is not possible in case if
command is run as non-ahriman user. This commit makes handle load lazy
and allows to run some commands as non-ahriman user
2022-12-31 14:48:21 +02:00
b44e5c3a3d Release 2.5.0 2022-12-31 02:02:37 +02:00
1891d6c944 force upgrade sphinx documentation 2022-12-30 22:58:13 +02:00
08d68258ca add completion files generated by shtab 2022-12-30 21:19:23 +02:00
cd3d12a754 allow 3 symbols for package search 2022-12-30 21:19:23 +02:00
81d9526054 improve VCS packages checks
* Unlike older version, currently service will always try to pull AUR
  package to check version. Previously if no-vcs flag is set, it would
  ignore VCS packages completelly
* Introduce build.vcs_allowed_age option. If set, it will skip version
  calculation if package age (now - build_date) is less than this value
2022-12-30 21:19:23 +02:00
c7447f19f0 change another boolean info option to boolean action 2022-12-29 03:15:48 +02:00
7de94a8daa add repo-structure subcommand
This commit also changes Tree class, replacing load method by resolve
2022-12-27 10:35:03 +02:00
aa1a59d0a3 fix property description in docs 2022-12-26 02:32:14 +02:00
8864855c14 write patches via gitremote push trigger (#79)
* write patches via gitremote push trigger

* implement context variables intead of custom database class
2022-12-25 02:10:38 +03:00
b0b37e8169 Release 2.4.1 2022-12-11 05:17:10 +02:00
7df4adfc9d replace alert bodals with toasts 2022-12-09 11:33:19 +02:00
2d5b73c6dc remove badges from status page 2022-12-09 00:58:03 +02:00
1c8aed4a97 check that package has been created during setup workflow 2022-12-06 11:09:56 +02:00
2e059023f3 Release 2.4.0 2022-12-05 00:25:53 +02:00
da5d97788a do not update database via pacman, use ahriman's own databses 2022-12-04 23:45:10 +02:00
0e839fbbf2 Some minor documentation related fixes
* Improve some wording (again)
* Change default type for refresh option to False (does not affect
  behavior)
* Update docstrings to reflect last changes
* Configuration.__convert_path has been replaced by shlex
* aiosecurity functions support kwargs now
2022-12-04 02:10:46 +02:00
262462d3c3 improve wording in documentation 2022-12-02 15:45:01 +02:00
7aa91f9e2e do not trigger update on sign command 2022-12-02 01:41:23 +02:00
01eda513cf improve setup command by --makeflags-jobs argument and fix repository sign on creation 2022-12-02 01:41:23 +02:00
0161617e36 implement support of unix socket for server
This feature can be used for unauthorized access to apis - e.g. for
reporting service if it is run on the same machine. Since now it becomes
recommended way for the interprocess communication, thus some options
(e.g. creating user with as-service flag) are no longer available now
2022-12-02 01:41:23 +02:00
4811dec759 do not invoke configuration write in case if no salt or user was written 2022-12-02 01:41:23 +02:00
09623c20d5 add rebuild implementation to interface 2022-12-02 01:41:23 +02:00
eba247b759 make package actions as dropdown 2022-12-02 01:41:23 +02:00
5073c80af1 add key-import button to interface 2022-12-02 01:41:23 +02:00
766081d212 add demos links 2022-11-24 02:38:33 +02:00
896cd0bd71 add security notes 2022-11-24 02:38:33 +02:00
ce1bd2f2db add curl examples to web views 2022-11-24 02:38:33 +02:00
6ba96d838d build docs together with web views 2022-11-24 02:38:33 +02:00
df9e03f495 change respone for service requests 2022-11-24 02:38:33 +02:00
89944eb2b6 add fallback for copying to clipboard 2022-11-24 02:38:33 +02:00
336784519b add show/hide password button 2022-11-24 02:38:33 +02:00
f3341ec7cd update web preview picture 2022-11-24 02:38:33 +02:00
d36e851a29 render httpunauthorized as html in plain http requests 2022-11-22 23:40:27 +02:00
28bd5f2095 allow scrolling with fixed copy button position 2022-11-22 22:20:21 +02:00
011b4e2e31 change logging module imports 2022-11-22 22:20:21 +02:00
15609ba044 change wording for package actions 2022-11-22 15:46:49 +02:00
6d4f9981f7 fix login and logout buttons decorations 2022-11-22 11:17:59 +02:00
bbb97d1cdd add more notes about docker 2022-11-22 10:49:46 +02:00
3bca780bdd check log record in handler instead of client 2022-11-22 02:30:37 +02:00
78e6b48c24 ask users to repeat password
In case if password is asked via getpass, it is possible to make typo
and user will not see the mistake. In order to avoid it, additional
confirmation has been added
2022-11-22 02:19:37 +02:00
137d62e2f8 Extended package status page (#76)
* implement log storage at backend
* handle process id during removal. During one process we can write logs from different packages in different times (e.g. check and update later) and we would like to store all logs belong to the same process
* set package context in main functions
* implement logs support in interface
* filter out logs posting http logs
* add timestamp to log records
* hide getting logs under reporter permission

List of breaking changes:

* `ahriman.core.lazy_logging.LazyLogging` has been renamed to `ahriman.core.log.LazyLogging`
* `ahriman.core.configuration.Configuration.from_path` does not have `quiet` attribute now
* `ahriman.core.configuration.Configuration` class does not have `load_logging` method now
* `ahriman.core.status.client.Client.load` requires `report` argument now
2022-11-22 02:58:22 +03:00
8a6854c867 Release 2.3.0 2022-11-16 00:53:48 +02:00
299732181c remote threadname from logging
Since application is mostly singlethreaded it makes no sense to log it
2022-11-14 23:21:06 +02:00
84c1b4d82d Release 2.3.0rc4 2022-11-14 01:02:27 +02:00
cdd66ee780 fix case when no files were commited in remote push trigger
The issue appears together with --intent-to-add flag for adding new
files. Original testing has been performed by having already added new
files, thus it passed all checks.

This commit also adds `commit_author` option which will allow to
overwrite the author.
2022-11-14 00:59:43 +02:00
b2ed383de0 Release 2.3.0rc3 2022-11-11 21:20:55 +02:00
551ee670bf rollback cwd parameter for clone 2022-11-11 21:19:27 +02:00
5d4bd9e459 Release 2.3.0rc2 2022-11-11 17:25:11 +02:00
4f21eb6fe6 Fix issue when there is no cached source directory yet (closes #75) 2022-11-11 17:23:15 +02:00
495 changed files with 24920 additions and 10313 deletions

View File

@ -1 +1 @@
skips: ['B101', 'B105', 'B106', 'B404']
skips: ['B101', 'B104', 'B105', 'B106', 'B404']

View File

@ -0,0 +1,20 @@
---
name: Security report
about: Create a report related to security issues
title: ''
labels: security
assignees: ''
---
## Summary
A clear and concise description of what the issue is.
### Steps to reproduce
Steps to reproduce the behavior (commands, environment etc).
### Intended impact
Brief optional description of how this vulnerability can be used and which effects can be achieved.

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-aur python-passlib 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
@ -38,7 +38,9 @@ systemd-machine-id-setup
sed -i "s/handlers = syslog_handler/handlers = console_handler/g" /etc/ahriman.ini.d/logging.ini
# initial setup command as root
[[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080")
ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "github" "${WEB_ARGS[@]}"
ahriman -a x86_64 service-setup --packager "ahriman bot <ahriman@example.com>" --repository "github" "${WEB_ARGS[@]}"
# validate configuration
ahriman -a x86_64 service-config-validate --exit-code
# enable services
systemctl enable ahriman-web@x86_64
systemctl enable ahriman@x86_64.timer
@ -52,7 +54,7 @@ fi
# the build itself does not really work in the container
sudo -u ahriman -- ahriman package-add --now yay
# check if package was actually installed
#test -n "$(find "/var/lib/ahriman/repository/x86_64" -name "yay*pkg*")"
test -n "$(find "/var/lib/ahriman/repository/x86_64" -name "yay*pkg*")"
# run package check
sudo -u ahriman -- ahriman repo-update
# stop web service lol

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

@ -533,5 +533,5 @@ valid-metaclass-classmethod-first-arg=cls
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception
overgeneral-exceptions=builtins.BaseException,
builtins.Exception

View File

@ -1,15 +1,17 @@
version: 2
formats: all
formats:
- pdf
build:
os: ubuntu-20.04
tools:
python: "3.10"
python: "3.11"
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true
python:
install:
@ -17,4 +19,6 @@ python:
path: .
extra_requirements:
- docs
- s3
- web
system_packages: true

View File

@ -1,2 +1,2 @@
Current developers:
Evgenii Alekseev aka arcanis <esalexeev (at) gmail (dot) com>
Current maintainer:
Evgenii Alekseev <esalexeev (at) gmail (dot) com>

View File

@ -26,7 +26,106 @@ In order to resolve all difficult cases the `autopep8` is used. You can perform
Again, the most checks can be performed by `make check` command, though some additional guidelines must be applied:
* Every class, every function (including private and protected), every attribute must be documented. The project follows [Google style documentation](https://google.github.io/styleguide/pyguide.html). The only exception is local functions.
* Type annotations are the must, even for local functions.
* Correct way to document function, if section is empty, e.g. no notes or there are no args, it should be omitted:
```python
def foo(argument: str, *, flag: bool = False) -> int:
"""
do foo. With very very very long
docstring
Notes:
Very important note about this function
Args:
argument(str): an argument. This argument has
long description also
flag(bool, optional): a flag (Default value = False)
Returns:
int: result
Raises:
RuntimeException: a local function error occurs
Examples:
Very informative example how to use this function, e.g.::
>>> foo("argument", flag=False)
Note that function documentation is in rST.
"""
```
`Returns` should be replaced with `Yields` for generators.
Class attributes should be documented in the following way:
```python
class Clazz(BaseClazz):
"""
brand-new implementation of ``BaseClazz``
Attributes:
CLAZZ_ATTRIBUTE(int): (class attribute) a brand-new class attribute
instance_attribute(str): an instance attribute
Examples:
Very informative class usage example, e.g.::
>>> from module import Clazz
>>> clazz = Clazz()
"""
CLAZZ_ATTRIBUTE = 42
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
default constructor
Args:
*args(Any): positional arguments
**kwargs(Any): keyword arguments
"""
self.instance_attribute = ""
```
* 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
class Clazz:
def __init__(self) -> None: ... # replace with `__post_init__` for dataclasses
@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) -> Self: ...
@staticmethod
def static_method() -> Any: ...
def __private_method(self) -> Any: ...
def _protected_method(self) -> Any: ...
def usual_method(self) -> Any: ...
def __hash__(self) -> int: ... # basically any magic (or look-alike) method
```
Methods inside one group should be ordered alphabetically, the only exception is `__init__` method (`__post__init__` for dataclasses) which should be defined first. For test methods it is recommended to follow the order in which functions are defined.
Though, we would like to highlight abstract methods (i.e. ones which raise `NotImplementedError`), we still keep in global order at the moment.
* Abstract methods must raise `NotImplementedError` instead of using `abc.abstractmethod`. The reason behind this restriction is the fact that we have class/static abstract methods for those we need to define their attribute first making the code harder to read.
* For any path interactions `pathlib.Path` must be used.
* Configuration interactions must go through `ahriman.core.configuration.Configuration` class instance.
* In case if class load requires some actions, it is recommended to create class method which can be used for class instantiating.
@ -59,9 +158,55 @@ Again, the most checks can be performed by `make check` command, though some add
* One file should define only one class, exception is class satellites in case if file length remains less than 400 lines.
* It is possible to create file which contains some functions (e.g. `ahriman.core.util`), but in this case you would need to define `__all__` attribute.
* The file size mentioned above must be applicable in general. In case of big classes consider splitting them into traits. Note, however, that `pylint` includes comments and docstrings into counter, thus you need to check file size by other tools.
* No global variable is allowed outside of `ahriman.version` module.
* 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.lazy_logging.LazyLogging` trait must be used.
* 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

@ -5,12 +5,16 @@ ENV AHRIMAN_ARCHITECTURE="x86_64"
ENV AHRIMAN_DEBUG=""
ENV AHRIMAN_FORCE_ROOT=""
ENV AHRIMAN_HOST="0.0.0.0"
ENV AHRIMAN_OUTPUT="syslog"
ENV AHRIMAN_MULTILIB="yes"
ENV AHRIMAN_OUTPUT=""
ENV AHRIMAN_PACKAGER="ahriman bot <ahriman@example.com>"
ENV AHRIMAN_PACMAN_MIRROR=""
ENV AHRIMAN_PORT=""
ENV AHRIMAN_REPOSITORY="aur-clone"
ENV AHRIMAN_REPOSITORY_ROOT="/var/lib/ahriman/ahriman"
ENV AHRIMAN_UNIX_SOCKET=""
ENV AHRIMAN_USER="ahriman"
ENV AHRIMAN_VALIDATE_CONFIGURATION="yes"
# install environment
## update pacman.conf with multilib
@ -24,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-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 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
@ -44,10 +45,14 @@ 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
COPY "docker/entrypoint.sh" "/usr/local/bin/entrypoint"
ENTRYPOINT ["entrypoint"]
# default command
CMD ["repo-update"]
CMD ["repo-update", "--refresh"]

View File

@ -3,7 +3,7 @@
PROJECT := ahriman
FILES := AUTHORS CONTRIBUTING.md COPYING README.md docs package src setup.py tox.ini web.png
FILES := AUTHORS CONTRIBUTING.md COPYING Makefile README.md SECURITY.md docs package src setup.py tox.ini web.png
TARGET_FILES := $(addprefix $(PROJECT)/, $(FILES))
IGNORE_FILES := package/archlinux src/.mypy_cache
@ -38,7 +38,7 @@ html: specification
tox -e docs-html
push: specification archlinux
git add package/archlinux/PKGBUILD src/ahriman/version.py docs/ahriman-architecture.svg docs/ahriman.1
git add package/archlinux/PKGBUILD src/ahriman/version.py docs/ahriman-architecture.svg docs/ahriman.1 docs/completions/
git commit -m "Release $(VERSION)"
git tag "$(VERSION)"
git push

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:
@ -33,3 +33,9 @@ Every available option is described in the [documentation](https://ahriman.readt
The application provides reasonable defaults which allow to use it out-of-box; however additional steps (like configuring build toolchain and sudoers) are recommended and can be easily achieved by following install instructions.
## [FAQ](https://ahriman.readthedocs.io/en/latest/faq.html)
## 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. [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).

9
SECURITY.md Normal file
View File

@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
The project follows bleeding edge philosophy, thus only the latest version is supported with the exception for release candidates (i.e. tags which are marked with `rc` suffix).
## Reporting a Vulnerability
In the most cases you can report (suspected) security vulnerabilities directly on github by using ["Security report" template](https://github.com/arcan1s/ahriman/issues/new?assignees=&labels=security&template=02-security-report.md&title=). However, if your report could lead to data leak or break the system we kindly ask you to contact [current maintainer](AUTHORS) directly by email.

View File

@ -4,36 +4,53 @@ set -e
[ -n "$AHRIMAN_DEBUG" ] && set -x
# configuration tune
sed -i "s|root = /var/lib/ahriman|root = $AHRIMAN_REPOSITORY_ROOT|g" "/etc/ahriman.ini"
sed -i "s|database = /var/lib/ahriman/ahriman.db|database = $AHRIMAN_REPOSITORY_ROOT/ahriman.db|g" "/etc/ahriman.ini"
sed -i "s|host = 127.0.0.1|host = $AHRIMAN_HOST|g" "/etc/ahriman.ini"
sed -i "s|handlers = syslog_handler|handlers = ${AHRIMAN_OUTPUT}_handler|g" "/etc/ahriman.ini.d/logging.ini"
cat <<EOF > "/etc/ahriman.ini.d/00-docker.ini"
[repository]
root = $AHRIMAN_REPOSITORY_ROOT
[settings]
database = $AHRIMAN_REPOSITORY_ROOT/ahriman.db
[web]
host = $AHRIMAN_HOST
EOF
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
[ -d "$AHRIMAN_REPOSITORY_ROOT" ] || mkdir "$AHRIMAN_REPOSITORY_ROOT"
chown "$AHRIMAN_USER":"$AHRIMAN_USER" "$AHRIMAN_REPOSITORY_ROOT"
# create .gnupg directory which is required for keys
AHRIMAN_GNUPG_HOME="$(getent passwd "$AHRIMAN_USER" | cut -d : -f 6)/.gnupg"
[ -d "$AHRIMAN_GNUPG_HOME" ] || mkdir -m700 "$AHRIMAN_GNUPG_HOME"
chown "$AHRIMAN_USER":"$AHRIMAN_USER" "$AHRIMAN_GNUPG_HOME"
# run built-in setup command
AHRIMAN_SETUP_ARGS=("--build-as-user" "$AHRIMAN_USER")
AHRIMAN_SETUP_ARGS+=("--packager" "$AHRIMAN_PACKAGER")
AHRIMAN_SETUP_ARGS+=("--repository" "$AHRIMAN_REPOSITORY")
if [ -z "$AHRIMAN_MULTILIB" ]; then
AHRIMAN_SETUP_ARGS+=("--no-multilib")
fi
if [ -n "$AHRIMAN_PACMAN_MIRROR" ]; then
AHRIMAN_SETUP_ARGS+=("--mirror" "$AHRIMAN_PACMAN_MIRROR")
fi
if [ -n "$AHRIMAN_PORT" ]; then
# in addition it must be handled in docker run command
AHRIMAN_SETUP_ARGS+=("--web-port" "$AHRIMAN_PORT")
fi
ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" repo-setup "${AHRIMAN_SETUP_ARGS[@]}"
if [ -n "$AHRIMAN_UNIX_SOCKET" ]; then
AHRIMAN_SETUP_ARGS+=("--web-unix-socket" "$AHRIMAN_UNIX_SOCKET")
fi
ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" service-setup "${AHRIMAN_SETUP_ARGS[@]}"
# validate configuration if set
[ -n "$AHRIMAN_VALIDATE_CONFIGURATION" ] && ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" service-config-validate --exit-code
# refresh database
pacman -Syy &> /dev/null
# create machine-id which is required by build tools
systemd-machine-id-setup &> /dev/null
@ -41,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")

View File

@ -11,8 +11,8 @@ Depending on the goal the package can be used in different ways. Nevertheless, i
from ahriman.core.database import SQLite
architecture = "x86_64"
configuration = Configuration.from_path(Path("/etc/ahriman.ini"), architecture, quiet=False)
sqlite = SQLite.load(configuration)
configuration = Configuration.from_path(Path("/etc/ahriman.ini"), architecture)
database = SQLite.load(configuration)
At this point there are ``configuration`` and ``database`` instances which can be used later at any time anywhere, e.g.
@ -27,7 +27,7 @@ Almost all actions are wrapped by ``ahriman.core.repository.Repository`` class
from ahriman.core.repository import Repository
repository = Repository(architecture, configuration, database, no_report=False, unsafe=False)
repository = Repository(architecture, configuration, database, report=True, unsafe=False)
And the ``repository`` instance can be used to perform repository maintenance
@ -37,6 +37,6 @@ And the ``repository`` instance can be used to perform repository maintenance
built_packages = repository.packages_built()
update_result = repository.process_update(built_packages)
repository.process_triggers(update_result)
repository.triggers.on_result(update_result, repository.packages())
For the more info please refer to the classes documentation.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 613 KiB

After

Width:  |  Height:  |  Size: 809 KiB

View File

@ -1,16 +1,16 @@
.TH AHRIMAN "1" "2022\-11\-11" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2023\-07\-28" "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,daemon,help,help-commands-unsafe,key-import,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-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-triggers,repo-update,update,shell,user-add,user-list,user-remove,version,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
.SH OPTIONS
.TP
\fB\-a\fR \fI\,ARCHITECTURE\/\fR, \fB\-\-architecture\fR \fI\,ARCHITECTURE\/\fR
target architectures (can be used multiple times)
target architectures. For several subcommands it can be used multiple times
.TP
\fB\-c\fR \fI\,CONFIGURATION\/\fR, \fB\-\-configuration\fR \fI\,CONFIGURATION\/\fR
@ -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
@ -46,17 +50,17 @@ COMMAND
\fBahriman\fR \fI\,aur\-search\/\fR
search for package
.TP
\fBahriman\fR \fI\,daemon\/\fR
run application as daemon
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help\-commands\-unsafe\/\fR
list unsafe commands
.TP
\fBahriman\fR \fI\,key\-import\/\fR
import PGP key
\fBahriman\fR \fI\,help\-updates\/\fR
check for service updates
.TP
\fBahriman\fR \fI\,help\-version\/\fR
application version
.TP
\fBahriman\fR \fI\,package\-add\/\fR
add package
@ -91,11 +95,14 @@ backup repository data
\fBahriman\fR \fI\,repo\-check\/\fR
check for updates
.TP
\fBahriman\fR \fI\,repo\-clean\/\fR
clean local caches
\fBahriman\fR \fI\,repo\-create\-keyring\/\fR
create keyring package
.TP
\fBahriman\fR \fI\,repo\-config\/\fR
dump configuration
\fBahriman\fR \fI\,repo\-create\-mirrorlist\/\fR
create mirrorlist package
.TP
\fBahriman\fR \fI\,repo\-daemon\/\fR
run application as daemon
.TP
\fBahriman\fR \fI\,repo\-rebuild\/\fR
rebuild repository
@ -109,9 +116,6 @@ generate report
\fBahriman\fR \fI\,repo\-restore\/\fR
restore repository data
.TP
\fBahriman\fR \fI\,repo\-setup\/\fR
initial service configuration
.TP
\fBahriman\fR \fI\,repo\-sign\/\fR
sign packages
.TP
@ -121,14 +125,32 @@ update repository status
\fBahriman\fR \fI\,repo\-sync\/\fR
sync repository
.TP
\fBahriman\fR \fI\,repo\-tree\/\fR
dump repository tree
.TP
\fBahriman\fR \fI\,repo\-triggers\/\fR
run triggers
.TP
\fBahriman\fR \fI\,repo\-update\/\fR
update packages
.TP
\fBahriman\fR \fI\,shell\/\fR
envoke python shell
\fBahriman\fR \fI\,service\-clean\/\fR
clean local caches
.TP
\fBahriman\fR \fI\,service\-config\/\fR
dump configuration
.TP
\fBahriman\fR \fI\,service\-config\-validate\/\fR
validate system configuration
.TP
\fBahriman\fR \fI\,service\-key\-import\/\fR
import PGP key
.TP
\fBahriman\fR \fI\,service\-setup\/\fR
initial service configuration
.TP
\fBahriman\fR \fI\,service\-shell\/\fR
invoke python shell
.TP
\fBahriman\fR \fI\,user\-add\/\fR
create or update user
@ -139,15 +161,12 @@ user known users and their access
\fBahriman\fR \fI\,user\-remove\/\fR
remove user
.TP
\fBahriman\fR \fI\,version\/\fR
application version
.TP
\fBahriman\fR \fI\,web\/\fR
web server
.SH COMMAND \fI\,'ahriman aur\-search'\/\fR
usage: ahriman aur\-search [\-h] [\-e] [\-\-info | \-\-no\-info]
[\-\-sort\-by {description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}]
[\-\-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 [search ...]
search for package in AUR using API
@ -163,44 +182,13 @@ 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,url,url_path,version}\/\fR
\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
sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted
by name
.SH COMMAND \fI\,'ahriman daemon'\/\fR
usage: ahriman daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
[\-\-vcs | \-\-no\-vcs] [\-y]
start process which periodically will run update process
.SH OPTIONS \fI\,'ahriman daemon'\/\fR
.TP
\fB\-i\fR \fI\,INTERVAL\/\fR, \fB\-\-interval\fR \fI\,INTERVAL\/\fR
interval between runs in seconds
.TP
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates. Implies \-\-no\-vcs (default: True)
.TP
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates (default: True)
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
include or exclude manual updates (default: True)
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
enable or disable checking of VCS packages (default: True)
.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 help'\/\fR
usage: ahriman help [\-h] [command]
@ -211,33 +199,33 @@ 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
.SH COMMAND \fI\,'ahriman key\-import'\/\fR
usage: ahriman key\-import [\-h] [\-\-key\-server KEY_SERVER] key
.SH COMMAND \fI\,'ahriman help\-updates'\/\fR
usage: ahriman help\-updates [\-h] [\-e]
import PGP key from public sources to the repository user
request AUR for current version and compare with current service version
.SH OPTIONS \fI\,'ahriman help\-updates'\/\fR
.TP
\fBkey\fR
PGP key to import from public server
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit code if updates available
.SH OPTIONS \fI\,'ahriman key\-import'\/\fR
.TP
\fB\-\-key\-server\fR \fI\,KEY_SERVER\/\fR
key server for key import
.SH COMMAND \fI\,'ahriman help\-version'\/\fR
usage: ahriman help\-version [\-h]
print application and its dependencies versions
.SH COMMAND \fI\,'ahriman package\-add'\/\fR
usage: ahriman package\-add [\-h] [\-e] [\-n] [\-y] [\-s {auto,archive,aur,directory,local,remote,repository}]
[\-\-without\-dependencies]
usage: ahriman package\-add [\-h] [\-\-dependencies | \-\-no\-dependencies] [\-e] [\-n] [\-y]
[\-s {auto,archive,aur,directory,local,remote,repository}] [\-u USERNAME]
package [package ...]
add existing or new package to the build queue
@ -247,6 +235,10 @@ add existing or new package to the build queue
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
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
@ -264,8 +256,8 @@ download fresh package databases from the mirror before actions, \-yy to force r
explicitly specify the package source for this command
.TP
\fB\-\-without\-dependencies\fR
do not add dependencies
\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 ...]
@ -277,7 +269,8 @@ remove package from the repository
package name or base
.SH COMMAND \fI\,'ahriman package\-status'\/\fR
usage: ahriman package\-status [\-h] [\-\-ahriman] [\-e] [\-i] [\-s {unknown,pending,building,failed,success}] [package ...]
usage: ahriman package\-status [\-h] [\-\-ahriman] [\-e] [\-\-info | \-\-no\-info] [\-s {unknown,pending,building,failed,success}]
[package ...]
request status of the package
@ -295,7 +288,7 @@ get service status itself
return non\-zero exit status if result is empty
.TP
\fB\-i\fR, \fB\-\-info\fR
\fB\-\-info\fR, \fB\-\-no\-info\fR
show additional package information
.TP
@ -414,46 +407,60 @@ return non\-zero exit status if result is empty
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
enable or disable checking 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\-clean'\/\fR
usage: ahriman repo\-clean [\-h] [\-\-cache | \-\-no\-cache] [\-\-chroot | \-\-no\-chroot] [\-\-manual | \-\-no\-manual]
[\-\-packages | \-\-no\-packages] [\-\-pacman | \-\-no\-pacman]
.SH COMMAND \fI\,'ahriman repo\-create\-keyring'\/\fR
usage: ahriman repo\-create\-keyring [\-h]
remove local caches
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 OPTIONS \fI\,'ahriman repo\-clean'\/\fR
.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]
start process which periodically will run update process
.SH OPTIONS \fI\,'ahriman repo\-daemon'\/\fR
.TP
\fB\-\-cache\fR, \fB\-\-no\-cache\fR
clear directory with package caches (default: False)
\fB\-i\fR \fI\,INTERVAL\/\fR, \fB\-\-interval\fR \fI\,INTERVAL\/\fR
interval between runs in seconds
.TP
\fB\-\-chroot\fR, \fB\-\-no\-chroot\fR
clear build chroot (default: False)
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies
.TP
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
clear manually added packages queue (default: False)
include or exclude manual updates
.TP
\fB\-\-packages\fR, \fB\-\-no\-packages\fR
clear directory with built packages (default: False)
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
fetch actual version of VCS packages
.TP
\fB\-\-pacman\fR, \fB\-\-no\-pacman\fR
clear directory with pacman local database cache (default: False)
.SH COMMAND \fI\,'ahriman repo\-config'\/\fR
usage: ahriman repo\-config [\-h]
dump configuration for the specified architecture
\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\-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
@ -476,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]
@ -505,51 +520,6 @@ path of the input archive
\fB\-o\fR \fI\,OUTPUT\/\fR, \fB\-\-output\fR \fI\,OUTPUT\/\fR
root path of the extracted files
.SH COMMAND \fI\,'ahriman repo\-setup'\/\fR
usage: ahriman repo\-setup [\-h] [\-\-build\-as\-user BUILD_AS_USER] [\-\-build\-command BUILD_COMMAND]
[\-\-from\-configuration FROM_CONFIGURATION] [\-\-multilib | \-\-no\-multilib] \-\-packager PACKAGER
\-\-repository REPOSITORY [\-\-sign\-key SIGN_KEY] [\-\-sign\-target {disabled,pacakges,repository}]
[\-\-web\-port WEB_PORT]
create initial service configuration, requires root
.SH OPTIONS \fI\,'ahriman repo\-setup'\/\fR
.TP
\fB\-\-build\-as\-user\fR \fI\,BUILD_AS_USER\/\fR
force makepkg user to the specific one
.TP
\fB\-\-build\-command\fR \fI\,BUILD_COMMAND\/\fR
build command prefix
.TP
\fB\-\-from\-configuration\fR \fI\,FROM_CONFIGURATION\/\fR
path to default devtools pacman configuration
.TP
\fB\-\-multilib\fR, \fB\-\-no\-multilib\fR
add or do not multilib repository (default: True)
.TP
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
packager name and email
.TP
\fB\-\-repository\fR \fI\,REPOSITORY\/\fR
repository name
.TP
\fB\-\-sign\-key\fR \fI\,SIGN_KEY\/\fR
sign key id
.TP
\fB\-\-sign\-target\fR \fI\,{disabled,pacakges,repository}\/\fR
sign options
.TP
\fB\-\-web\-port\fR \fI\,WEB_PORT\/\fR
port of the web service
.SH COMMAND \fI\,'ahriman repo\-sign'\/\fR
usage: ahriman repo\-sign [\-h] [package ...]
@ -574,6 +544,11 @@ usage: ahriman repo\-sync [\-h]
sync repository files to remote server according to current settings
.SH COMMAND \fI\,'ahriman repo\-tree'\/\fR
usage: ahriman repo\-tree [\-h]
dump repository tree based on packages dependencies
.SH COMMAND \fI\,'ahriman repo\-triggers'\/\fR
usage: ahriman repo\-triggers [\-h] [trigger ...]
@ -581,11 +556,11 @@ run triggers on empty build result as configured by settings
.TP
\fBtrigger\fR
instead of running all triggers as set by configuration, just process specified ones oin order of metion
instead of running all triggers as set by configuration, just process specified ones in order of mention
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
usage: ahriman repo\-update [\-h] [\-\-dry\-run] [\-e] [\-\-aur | \-\-no\-aur] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
[\-\-vcs | \-\-no\-vcs] [\-y]
usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-e]
[\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
[package ...]
check for packages updates and run build process if requested
@ -595,6 +570,14 @@ check for packages updates and run build process if requested
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
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies
.TP
\fB\-\-dry\-run\fR
just perform check for updates, same as check command
@ -603,37 +586,162 @@ just perform check for updates, same as check command
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates. Implies \-\-no\-vcs (default: True)
.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
enable or disable checking 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 shell'\/\fR
usage: ahriman shell [\-h] [code]
.SH COMMAND \fI\,'ahriman service\-clean'\/\fR
usage: ahriman service\-clean [\-h] [\-\-cache | \-\-no\-cache] [\-\-chroot | \-\-no\-chroot] [\-\-manual | \-\-no\-manual]
[\-\-packages | \-\-no\-packages] [\-\-pacman | \-\-no\-pacman]
drop into python shell while having created application
remove local caches
.SH OPTIONS \fI\,'ahriman service\-clean'\/\fR
.TP
\fB\-\-cache\fR, \fB\-\-no\-cache\fR
clear directory with package caches
.TP
\fB\-\-chroot\fR, \fB\-\-no\-chroot\fR
clear build chroot
.TP
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
clear manually added packages queue
.TP
\fB\-\-packages\fR, \fB\-\-no\-packages\fR
clear directory with built packages
.TP
\fB\-\-pacman\fR, \fB\-\-no\-pacman\fR
clear directory with pacman local database cache
.SH COMMAND \fI\,'ahriman service\-config'\/\fR
usage: ahriman service\-config [\-h] [\-\-secure | \-\-no\-secure]
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
.SH COMMAND \fI\,'ahriman service\-config\-validate'\/\fR
usage: ahriman service\-config\-validate [\-h] [\-e]
validate configuration and print found errors
.SH OPTIONS \fI\,'ahriman service\-config\-validate'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if configuration is invalid
.SH COMMAND \fI\,'ahriman service\-key\-import'\/\fR
usage: ahriman service\-key\-import [\-h] [\-\-key\-server KEY_SERVER] key
import PGP key from public sources to the repository user
.TP
\fBkey\fR
PGP key to import from public server
.SH OPTIONS \fI\,'ahriman service\-key\-import'\/\fR
.TP
\fB\-\-key\-server\fR \fI\,KEY_SERVER\/\fR
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] [\-\-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
.SH OPTIONS \fI\,'ahriman service\-setup'\/\fR
.TP
\fB\-\-build\-as\-user\fR \fI\,BUILD_AS_USER\/\fR
force makepkg user to the specific one
.TP
\fB\-\-build\-command\fR \fI\,BUILD_COMMAND\/\fR
build command prefix
.TP
\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
.TP
\fB\-\-mirror\fR \fI\,MIRROR\/\fR
use the specified explicitly mirror instead of including mirrorlist
.TP
\fB\-\-multilib\fR, \fB\-\-no\-multilib\fR
add or do not multilib repository
.TP
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
packager name and email
.TP
\fB\-\-repository\fR \fI\,REPOSITORY\/\fR
repository name
.TP
\fB\-\-sign\-key\fR \fI\,SIGN_KEY\/\fR
sign key id
.TP
\fB\-\-sign\-target\fR \fI\,{disabled,packages,repository}\/\fR
sign options
.TP
\fB\-\-web\-port\fR \fI\,WEB_PORT\/\fR
port of the web service
.TP
\fB\-\-web\-unix\-socket\fR \fI\,WEB_UNIX_SOCKET\/\fR
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
.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] [\-\-as\-service] [\-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
@ -643,8 +751,12 @@ username for web service
.SH OPTIONS \fI\,'ahriman user\-add'\/\fR
.TP
\fB\-\-as\-service\fR
add user as service user
\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
@ -655,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]
@ -678,7 +786,7 @@ return non\-zero exit status if result is empty
filter users by role
.SH COMMAND \fI\,'ahriman user\-remove'\/\fR
usage: ahriman user\-remove [\-h] [\-s] username
usage: ahriman user\-remove [\-h] username
remove user from the user mapping and update the configuration
@ -686,16 +794,6 @@ remove user from the user mapping and update the configuration
\fBusername\fR
username for web service
.SH OPTIONS \fI\,'ahriman user\-remove'\/\fR
.TP
\fB\-s\fR, \fB\-\-secure\fR
set file permissions to user\-only
.SH COMMAND \fI\,'ahriman version'\/\fR
usage: ahriman version [\-h]
print application and its dependencies versions
.SH COMMAND \fI\,'ahriman web'\/\fR
usage: ahriman web [\-h]
@ -704,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

@ -116,6 +116,14 @@ ahriman.application.handlers.search module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.service\_updates module
----------------------------------------------------
.. automodule:: ahriman.application.handlers.service_updates
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.setup module
-----------------------------------------
@ -156,6 +164,14 @@ ahriman.application.handlers.status\_update module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.structure module
---------------------------------------------
.. automodule:: ahriman.application.handlers.structure
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.triggers module
--------------------------------------------
@ -188,6 +204,14 @@ ahriman.application.handlers.users module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.validate module
--------------------------------------------
.. automodule:: ahriman.application.handlers.validate
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.versions module
--------------------------------------------

View File

@ -0,0 +1,37 @@
ahriman.core.configuration package
==================================
Submodules
----------
ahriman.core.configuration.configuration module
-----------------------------------------------
.. automodule:: ahriman.core.configuration.configuration
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.configuration.schema module
----------------------------------------
.. automodule:: ahriman.core.configuration.schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.configuration.validator module
-------------------------------------------
.. automodule:: ahriman.core.configuration.validator
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.configuration
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,45 +0,0 @@
ahriman.core.database.data package
==================================
Submodules
----------
ahriman.core.database.data.package\_remotes module
--------------------------------------------------
.. automodule:: ahriman.core.database.data.package_remotes
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.data.package\_statuses module
---------------------------------------------------
.. automodule:: ahriman.core.database.data.package_statuses
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.data.patches module
-----------------------------------------
.. automodule:: ahriman.core.database.data.patches
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.data.users module
---------------------------------------
.. automodule:: ahriman.core.database.data.users
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.database.data
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -36,6 +36,46 @@ ahriman.core.database.migrations.m003\_patch\_variables module
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m004\_logs module
--------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m004_logs
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m005\_make\_opt\_depends module
----------------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m005_make_opt_depends
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m006\_packages\_architecture\_required module
------------------------------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m006_packages_architecture_required
:members:
: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.database.operations.build\_operations module
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.logs\_operations module
--------------------------------------------------------
.. automodule:: ahriman.core.database.operations.logs_operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.operations module
--------------------------------------------------

View File

@ -7,7 +7,6 @@ Subpackages
.. toctree::
:maxdepth: 4
ahriman.core.database.data
ahriman.core.database.migrations
ahriman.core.database.operations

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
-----------------------------------------------------
@ -68,6 +76,14 @@ ahriman.core.formatters.string\_printer module
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.tree\_printer module
--------------------------------------------
.. automodule:: ahriman.core.formatters.tree_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.update\_printer module
----------------------------------------------
@ -84,6 +100,14 @@ ahriman.core.formatters.user\_printer module
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.validation\_printer module
--------------------------------------------------
.. automodule:: ahriman.core.formatters.validation_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.version\_printer module
-----------------------------------------------

53
docs/ahriman.core.log.rst Normal file
View File

@ -0,0 +1,53 @@
ahriman.core.log package
========================
Submodules
----------
ahriman.core.log.filtered\_access\_logger module
------------------------------------------------
.. automodule:: ahriman.core.log.filtered_access_logger
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.log.http\_log\_handler module
------------------------------------------
.. automodule:: ahriman.core.log.http_log_handler
:members:
: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
-------------------------------------
.. automodule:: ahriman.core.log.lazy_logging
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.log.log module
---------------------------
.. automodule:: ahriman.core.log.log
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.log
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -10,27 +10,22 @@ Subpackages
ahriman.core.alpm
ahriman.core.auth
ahriman.core.build_tools
ahriman.core.configuration
ahriman.core.database
ahriman.core.formatters
ahriman.core.gitremote
ahriman.core.log
ahriman.core.report
ahriman.core.repository
ahriman.core.sign
ahriman.core.status
ahriman.core.support
ahriman.core.triggers
ahriman.core.upload
Submodules
----------
ahriman.core.configuration module
---------------------------------
.. automodule:: ahriman.core.configuration
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.exceptions module
------------------------------
@ -39,14 +34,6 @@ ahriman.core.exceptions module
:no-undoc-members:
:show-inheritance:
ahriman.core.lazy\_logging module
---------------------------------
.. automodule:: ahriman.core.lazy_logging
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.spawn module
-------------------------

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

@ -36,6 +36,14 @@ ahriman.models.build\_status module
:no-undoc-members:
:show-inheritance:
ahriman.models.context\_key module
----------------------------------
.. automodule:: ahriman.models.context_key
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.counters module
------------------------------
@ -52,6 +60,22 @@ 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
-------------------------------------
.. automodule:: ahriman.models.log_record_id
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.migration module
-------------------------------
@ -92,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
-------------------------------------
@ -180,14 +220,6 @@ ahriman.models.user\_access module
:no-undoc-members:
:show-inheritance:
ahriman.models.user\_identity module
------------------------------------
.. automodule:: ahriman.models.user_identity
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

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

@ -12,6 +12,22 @@ ahriman.web.views.service.add module
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.pgp module
------------------------------------
.. automodule:: ahriman.web.views.service.pgp
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.rebuild module
----------------------------------------
.. automodule:: ahriman.web.views.service.rebuild
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.remove module
---------------------------------------
@ -36,6 +52,14 @@ ahriman.web.views.service.search module
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.update module
---------------------------------------
.. automodule:: ahriman.web.views.service.update
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -4,6 +4,14 @@ ahriman.web.views.status package
Submodules
----------
ahriman.web.views.status.logs module
------------------------------------
.. automodule:: ahriman.web.views.status.logs
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.status.package module
---------------------------------------

View File

@ -28,24 +28,26 @@ This package contains application (aka executable) related classes and everythin
``ahriman.core`` package
^^^^^^^^^^^^^^^^^^^^^^^^
This package contains everything which is required for any time of application run and separated into several packages:
This package contains everything required for the most of application actions and it is separated into several packages:
* ``ahriman.core.alpm`` package controls pacman related functions. It provides wrappers for ``pyalpm`` library and safe calls for repository tools (``repo-add`` and ``repo-remove``). Also this package contains ``ahriman.core.alpm.remote`` package which provides wrapper for remote sources (e.g. AUR RPC and official repositories RPC).
* ``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.
* ``ahriman.core.log`` is a log utils package. It includes logger loader class, custom HTTP based logger and access logger for HTTP services with additional filters.
* ``ahriman.core.report`` is a package with reporting triggers. Should not be called directly.
* ``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.
@ -53,7 +55,7 @@ This package also provides some generic functions and classes which may be used
``ahriman.models`` package
^^^^^^^^^^^^^^^^^^^^^^^^^^
It provides models for any other part of application. Unlike ``ahriman.core`` package classes from here provides only conversion methods (e.g. create class from another or convert to). Mostly case classes and enumerations.
It provides models for any other part of application. Unlike ``ahriman.core`` package classes from here provide only conversion methods (e.g. create class from another or convert to). Mostly case classes and enumerations.
``ahriman.web`` package
^^^^^^^^^^^^^^^^^^^^^^^
@ -61,25 +63,44 @@ 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 most cases handlers spawn god class ``ahriman.application.application.Application`` class and call required methods.
In the most cases handlers spawn god class ``ahriman.application.application.Application`` class and call required methods.
Application is designed to run from ``systemd`` services and provides parametrized by architecture timer and service file for that.
Subcommand design
^^^^^^^^^^^^^^^^^
All subcommands are divided into several groups depending on the role they are doing:
* ``aur`` (``aur-search``) group is for AUR operations.
* ``help`` (e.g. ``help``) are system commands.
* ``package`` subcommands (e.g. ``package-add``) allow to perform single package actions.
* ``patch`` subcommands (e.g. ``pacth-list``) are the special case of ``package`` subcommands introduced in order to control patches for packages.
* ``repo`` subcommands (e.g. ``repo-check``) usually perform actions on whole repository.
* ``service`` subcommands (e.g. ``service-setup``) perform actions which are related to whole service managing: create repository, show configuration.
* ``user`` subcommands (``user-add``) are intended for user management.
* ``web`` subcommands are related to web service management.
For historical reasons and in order to keep backward compatibility some subcommands have aliases to their shorter forms or even other groups, but the service doesn't guarantee that they will remain unchanged.
Database
--------
@ -97,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.
There are also data migrations which are located at ``ahriman.core.database.data`` package and move data from old-style (e.g. json files in filesystem, directory trees, etc) to the database. They are also part of migration and (unlike schema migrations) are applied only at specific version breakpoints (e.g. if ``user_version`` is more than 0 no initial migration will be applied).
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -123,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
^^^^^^^^^^^^^^^
@ -168,6 +195,15 @@ Utils
For every external command run (which is actually not recommended if possible) custom wrapper for ``subprocess`` is used. Additional functions ``ahriman.core.auth.helpers`` provide safe calls for ``aiohttp_security`` methods and are required to make this dependency optional.
Context variables
^^^^^^^^^^^^^^^^^
Package provides implicit global variables which can be accessed from ``ahriman.core`` package as ``context`` variable, wrapped by ``contextvars.ContextVar`` class. The value of the variable is defaulting to private ``_Context`` class which is defined in the same module. The default values - such as ``database`` and ``sign`` - are being set on the service initialization.
The ``_Context`` class itself mimics default collection interface (as is Mapping) and can be modified by ``_Context.set`` method. The stored variables can be achieved by ``_Context.get`` method, which is unlike default ``Mapping`` interface also performs type and presence checks.
In order to provide statically typed interface, the ``ahriman.models.context_key.ContextKey`` class is used for both ``_Content.get`` and ``_Content.set`` methods; the context instance itself, however, does not store information about types.
Submodules
^^^^^^^^^^
@ -196,18 +232,20 @@ means that there is user ``username`` with ``read`` access and password ``passwo
OAuth provider uses library definitions (``aioauth-client``) in order *authenticate* users. It still requires user permission to be set in database, thus it inherits mapping provider without any changes. Whereas we could override ``check_credentials`` (authentication method) by something custom, OAuth flow is a bit more complex than just forward request, thus we have to implement the flow in login form.
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 is used by service reporting (aka robots).
OAuth's implementation also allows authenticating users via username + password (in the same way as mapping does) though it is not recommended for end-users and password must be left blank. In particular this feature can be used by service reporting (aka robots).
In addition, web service checks the source socket used. In case if it belongs to ``socket.AF_UNIX`` family, it will skip any further checks considering the request to be performed in safe environment (e.g. on the same physical machine). This feature, in particular is being used by the reporter instances in case if socket address is set in configuration.
In order to configure users there are special commands.
Triggers
^^^^^^^^
Triggers are extensions which can be used in order to perform any actions on application start, after the update process and, finally, before the application exit. The package provides two default extensions - one is report generation and another one is remote upload feature.
Triggers are extensions which can be used in order to perform any actions on application start, after the update process and, finally, before the application exit.
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.
@ -241,15 +279,24 @@ 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.
* In addition if you would like to disable authorization for local access (recommended way in order to run the application itself with reporting support), the ``requests-unixsocket`` library is required.
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
^^^^^^^^^
@ -259,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.
@ -281,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

@ -0,0 +1,589 @@
# 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-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' '--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')
_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' '-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')
_shtab_ahriman_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
_shtab_ahriman_package_status_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_patch_add_option_strings=('-h' '--help')
_shtab_ahriman_patch_list_option_strings=('-h' '--help' '-e' '--exit-code' '-v' '--variable')
_shtab_ahriman_patch_remove_option_strings=('-h' '--help' '-v' '--variable')
_shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
_shtab_ahriman_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' '-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')
_shtab_ahriman_report_option_strings=('-h' '--help')
_shtab_ahriman_repo_restore_option_strings=('-h' '--help' '-o' '--output')
_shtab_ahriman_repo_sign_option_strings=('-h' '--help')
_shtab_ahriman_sign_option_strings=('-h' '--help')
_shtab_ahriman_repo_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_repo_sync_option_strings=('-h' '--help')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_repo_tree_option_strings=('-h' '--help')
_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' '-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')
_shtab_ahriman_service_config_option_strings=('-h' '--help' '--secure' '--no-secure')
_shtab_ahriman_config_option_strings=('-h' '--help' '--secure' '--no-secure')
_shtab_ahriman_repo_config_option_strings=('-h' '--help' '--secure' '--no-secure')
_shtab_ahriman_service_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_repo_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_service_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_service_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' '--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-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')
_shtab_ahriman_package_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_update__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_update___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_rebuild__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_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')
_shtab_ahriman_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_repo_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_repo_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_user_add__r_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_add___role_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_list__r_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_list___role_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_pos_0_nargs=A...
_shtab_ahriman__h_nargs=0
_shtab_ahriman___help_nargs=0
_shtab_ahriman___force_nargs=0
_shtab_ahriman___report_nargs=0
_shtab_ahriman___no_report_nargs=0
_shtab_ahriman__q_nargs=0
_shtab_ahriman___quiet_nargs=0
_shtab_ahriman___unsafe_nargs=0
_shtab_ahriman__V_nargs=0
_shtab_ahriman___version_nargs=0
_shtab_ahriman_aur_search_pos_0_nargs=+
_shtab_ahriman_aur_search__h_nargs=0
_shtab_ahriman_aur_search___help_nargs=0
_shtab_ahriman_aur_search__e_nargs=0
_shtab_ahriman_aur_search___exit_code_nargs=0
_shtab_ahriman_aur_search___info_nargs=0
_shtab_ahriman_aur_search___no_info_nargs=0
_shtab_ahriman_search_pos_0_nargs=+
_shtab_ahriman_search__h_nargs=0
_shtab_ahriman_search___help_nargs=0
_shtab_ahriman_search__e_nargs=0
_shtab_ahriman_search___exit_code_nargs=0
_shtab_ahriman_search___info_nargs=0
_shtab_ahriman_search___no_info_nargs=0
_shtab_ahriman_help__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
_shtab_ahriman_help_updates___help_nargs=0
_shtab_ahriman_help_updates__e_nargs=0
_shtab_ahriman_help_updates___exit_code_nargs=0
_shtab_ahriman_help_version__h_nargs=0
_shtab_ahriman_help_version___help_nargs=0
_shtab_ahriman_version__h_nargs=0
_shtab_ahriman_version___help_nargs=0
_shtab_ahriman_package_add_pos_0_nargs=+
_shtab_ahriman_package_add__h_nargs=0
_shtab_ahriman_package_add___help_nargs=0
_shtab_ahriman_package_add___dependencies_nargs=0
_shtab_ahriman_package_add___no_dependencies_nargs=0
_shtab_ahriman_package_add__e_nargs=0
_shtab_ahriman_package_add___exit_code_nargs=0
_shtab_ahriman_package_add__n_nargs=0
_shtab_ahriman_package_add___now_nargs=0
_shtab_ahriman_package_add__y_nargs=0
_shtab_ahriman_package_add___refresh_nargs=0
_shtab_ahriman_add_pos_0_nargs=+
_shtab_ahriman_add__h_nargs=0
_shtab_ahriman_add___help_nargs=0
_shtab_ahriman_add___dependencies_nargs=0
_shtab_ahriman_add___no_dependencies_nargs=0
_shtab_ahriman_add__e_nargs=0
_shtab_ahriman_add___exit_code_nargs=0
_shtab_ahriman_add__n_nargs=0
_shtab_ahriman_add___now_nargs=0
_shtab_ahriman_add__y_nargs=0
_shtab_ahriman_add___refresh_nargs=0
_shtab_ahriman_package_update_pos_0_nargs=+
_shtab_ahriman_package_update__h_nargs=0
_shtab_ahriman_package_update___help_nargs=0
_shtab_ahriman_package_update___dependencies_nargs=0
_shtab_ahriman_package_update___no_dependencies_nargs=0
_shtab_ahriman_package_update__e_nargs=0
_shtab_ahriman_package_update___exit_code_nargs=0
_shtab_ahriman_package_update__n_nargs=0
_shtab_ahriman_package_update___now_nargs=0
_shtab_ahriman_package_update__y_nargs=0
_shtab_ahriman_package_update___refresh_nargs=0
_shtab_ahriman_package_remove_pos_0_nargs=+
_shtab_ahriman_package_remove__h_nargs=0
_shtab_ahriman_package_remove___help_nargs=0
_shtab_ahriman_remove_pos_0_nargs=+
_shtab_ahriman_remove__h_nargs=0
_shtab_ahriman_remove___help_nargs=0
_shtab_ahriman_package_status_pos_0_nargs=*
_shtab_ahriman_package_status__h_nargs=0
_shtab_ahriman_package_status___help_nargs=0
_shtab_ahriman_package_status___ahriman_nargs=0
_shtab_ahriman_package_status__e_nargs=0
_shtab_ahriman_package_status___exit_code_nargs=0
_shtab_ahriman_package_status___info_nargs=0
_shtab_ahriman_package_status___no_info_nargs=0
_shtab_ahriman_status_pos_0_nargs=*
_shtab_ahriman_status__h_nargs=0
_shtab_ahriman_status___help_nargs=0
_shtab_ahriman_status___ahriman_nargs=0
_shtab_ahriman_status__e_nargs=0
_shtab_ahriman_status___exit_code_nargs=0
_shtab_ahriman_status___info_nargs=0
_shtab_ahriman_status___no_info_nargs=0
_shtab_ahriman_package_status_remove_pos_0_nargs=+
_shtab_ahriman_package_status_remove__h_nargs=0
_shtab_ahriman_package_status_remove___help_nargs=0
_shtab_ahriman_package_status_update_pos_0_nargs=*
_shtab_ahriman_package_status_update__h_nargs=0
_shtab_ahriman_package_status_update___help_nargs=0
_shtab_ahriman_status_update_pos_0_nargs=*
_shtab_ahriman_status_update__h_nargs=0
_shtab_ahriman_status_update___help_nargs=0
_shtab_ahriman_patch_add__h_nargs=0
_shtab_ahriman_patch_add___help_nargs=0
_shtab_ahriman_patch_list__h_nargs=0
_shtab_ahriman_patch_list___help_nargs=0
_shtab_ahriman_patch_list__e_nargs=0
_shtab_ahriman_patch_list___exit_code_nargs=0
_shtab_ahriman_patch_remove__h_nargs=0
_shtab_ahriman_patch_remove___help_nargs=0
_shtab_ahriman_patch_set_add__h_nargs=0
_shtab_ahriman_patch_set_add___help_nargs=0
_shtab_ahriman_repo_backup__h_nargs=0
_shtab_ahriman_repo_backup___help_nargs=0
_shtab_ahriman_repo_check_pos_0_nargs=*
_shtab_ahriman_repo_check__h_nargs=0
_shtab_ahriman_repo_check___help_nargs=0
_shtab_ahriman_repo_check__e_nargs=0
_shtab_ahriman_repo_check___exit_code_nargs=0
_shtab_ahriman_repo_check___vcs_nargs=0
_shtab_ahriman_repo_check___no_vcs_nargs=0
_shtab_ahriman_repo_check__y_nargs=0
_shtab_ahriman_repo_check___refresh_nargs=0
_shtab_ahriman_check_pos_0_nargs=*
_shtab_ahriman_check__h_nargs=0
_shtab_ahriman_check___help_nargs=0
_shtab_ahriman_check__e_nargs=0
_shtab_ahriman_check___exit_code_nargs=0
_shtab_ahriman_check___vcs_nargs=0
_shtab_ahriman_check___no_vcs_nargs=0
_shtab_ahriman_check__y_nargs=0
_shtab_ahriman_check___refresh_nargs=0
_shtab_ahriman_repo_create_keyring__h_nargs=0
_shtab_ahriman_repo_create_keyring___help_nargs=0
_shtab_ahriman_repo_create_mirrorlist__h_nargs=0
_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
_shtab_ahriman_repo_daemon___no_aur_nargs=0
_shtab_ahriman_repo_daemon___dependencies_nargs=0
_shtab_ahriman_repo_daemon___no_dependencies_nargs=0
_shtab_ahriman_repo_daemon___local_nargs=0
_shtab_ahriman_repo_daemon___no_local_nargs=0
_shtab_ahriman_repo_daemon___manual_nargs=0
_shtab_ahriman_repo_daemon___no_manual_nargs=0
_shtab_ahriman_repo_daemon___vcs_nargs=0
_shtab_ahriman_repo_daemon___no_vcs_nargs=0
_shtab_ahriman_repo_daemon__y_nargs=0
_shtab_ahriman_repo_daemon___refresh_nargs=0
_shtab_ahriman_daemon__h_nargs=0
_shtab_ahriman_daemon___help_nargs=0
_shtab_ahriman_daemon___aur_nargs=0
_shtab_ahriman_daemon___no_aur_nargs=0
_shtab_ahriman_daemon___dependencies_nargs=0
_shtab_ahriman_daemon___no_dependencies_nargs=0
_shtab_ahriman_daemon___local_nargs=0
_shtab_ahriman_daemon___no_local_nargs=0
_shtab_ahriman_daemon___manual_nargs=0
_shtab_ahriman_daemon___no_manual_nargs=0
_shtab_ahriman_daemon___vcs_nargs=0
_shtab_ahriman_daemon___no_vcs_nargs=0
_shtab_ahriman_daemon__y_nargs=0
_shtab_ahriman_daemon___refresh_nargs=0
_shtab_ahriman_repo_rebuild__h_nargs=0
_shtab_ahriman_repo_rebuild___help_nargs=0
_shtab_ahriman_repo_rebuild___dry_run_nargs=0
_shtab_ahriman_repo_rebuild___from_database_nargs=0
_shtab_ahriman_repo_rebuild__e_nargs=0
_shtab_ahriman_repo_rebuild___exit_code_nargs=0
_shtab_ahriman_rebuild__h_nargs=0
_shtab_ahriman_rebuild___help_nargs=0
_shtab_ahriman_rebuild___dry_run_nargs=0
_shtab_ahriman_rebuild___from_database_nargs=0
_shtab_ahriman_rebuild__e_nargs=0
_shtab_ahriman_rebuild___exit_code_nargs=0
_shtab_ahriman_repo_remove_unknown__h_nargs=0
_shtab_ahriman_repo_remove_unknown___help_nargs=0
_shtab_ahriman_repo_remove_unknown___dry_run_nargs=0
_shtab_ahriman_remove_unknown__h_nargs=0
_shtab_ahriman_remove_unknown___help_nargs=0
_shtab_ahriman_remove_unknown___dry_run_nargs=0
_shtab_ahriman_repo_report__h_nargs=0
_shtab_ahriman_repo_report___help_nargs=0
_shtab_ahriman_report__h_nargs=0
_shtab_ahriman_report___help_nargs=0
_shtab_ahriman_repo_restore__h_nargs=0
_shtab_ahriman_repo_restore___help_nargs=0
_shtab_ahriman_repo_sign_pos_0_nargs=*
_shtab_ahriman_repo_sign__h_nargs=0
_shtab_ahriman_repo_sign___help_nargs=0
_shtab_ahriman_sign_pos_0_nargs=*
_shtab_ahriman_sign__h_nargs=0
_shtab_ahriman_sign___help_nargs=0
_shtab_ahriman_repo_status_update__h_nargs=0
_shtab_ahriman_repo_status_update___help_nargs=0
_shtab_ahriman_repo_sync__h_nargs=0
_shtab_ahriman_repo_sync___help_nargs=0
_shtab_ahriman_sync__h_nargs=0
_shtab_ahriman_sync___help_nargs=0
_shtab_ahriman_repo_tree__h_nargs=0
_shtab_ahriman_repo_tree___help_nargs=0
_shtab_ahriman_repo_triggers_pos_0_nargs=*
_shtab_ahriman_repo_triggers__h_nargs=0
_shtab_ahriman_repo_triggers___help_nargs=0
_shtab_ahriman_repo_update_pos_0_nargs=*
_shtab_ahriman_repo_update__h_nargs=0
_shtab_ahriman_repo_update___help_nargs=0
_shtab_ahriman_repo_update___aur_nargs=0
_shtab_ahriman_repo_update___no_aur_nargs=0
_shtab_ahriman_repo_update___dependencies_nargs=0
_shtab_ahriman_repo_update___no_dependencies_nargs=0
_shtab_ahriman_repo_update___dry_run_nargs=0
_shtab_ahriman_repo_update__e_nargs=0
_shtab_ahriman_repo_update___exit_code_nargs=0
_shtab_ahriman_repo_update___local_nargs=0
_shtab_ahriman_repo_update___no_local_nargs=0
_shtab_ahriman_repo_update___manual_nargs=0
_shtab_ahriman_repo_update___no_manual_nargs=0
_shtab_ahriman_repo_update___vcs_nargs=0
_shtab_ahriman_repo_update___no_vcs_nargs=0
_shtab_ahriman_repo_update__y_nargs=0
_shtab_ahriman_repo_update___refresh_nargs=0
_shtab_ahriman_update_pos_0_nargs=*
_shtab_ahriman_update__h_nargs=0
_shtab_ahriman_update___help_nargs=0
_shtab_ahriman_update___aur_nargs=0
_shtab_ahriman_update___no_aur_nargs=0
_shtab_ahriman_update___dependencies_nargs=0
_shtab_ahriman_update___no_dependencies_nargs=0
_shtab_ahriman_update___dry_run_nargs=0
_shtab_ahriman_update__e_nargs=0
_shtab_ahriman_update___exit_code_nargs=0
_shtab_ahriman_update___local_nargs=0
_shtab_ahriman_update___no_local_nargs=0
_shtab_ahriman_update___manual_nargs=0
_shtab_ahriman_update___no_manual_nargs=0
_shtab_ahriman_update___vcs_nargs=0
_shtab_ahriman_update___no_vcs_nargs=0
_shtab_ahriman_update__y_nargs=0
_shtab_ahriman_update___refresh_nargs=0
_shtab_ahriman_service_clean__h_nargs=0
_shtab_ahriman_service_clean___help_nargs=0
_shtab_ahriman_service_clean___cache_nargs=0
_shtab_ahriman_service_clean___no_cache_nargs=0
_shtab_ahriman_service_clean___chroot_nargs=0
_shtab_ahriman_service_clean___no_chroot_nargs=0
_shtab_ahriman_service_clean___manual_nargs=0
_shtab_ahriman_service_clean___no_manual_nargs=0
_shtab_ahriman_service_clean___packages_nargs=0
_shtab_ahriman_service_clean___no_packages_nargs=0
_shtab_ahriman_service_clean___pacman_nargs=0
_shtab_ahriman_service_clean___no_pacman_nargs=0
_shtab_ahriman_clean__h_nargs=0
_shtab_ahriman_clean___help_nargs=0
_shtab_ahriman_clean___cache_nargs=0
_shtab_ahriman_clean___no_cache_nargs=0
_shtab_ahriman_clean___chroot_nargs=0
_shtab_ahriman_clean___no_chroot_nargs=0
_shtab_ahriman_clean___manual_nargs=0
_shtab_ahriman_clean___no_manual_nargs=0
_shtab_ahriman_clean___packages_nargs=0
_shtab_ahriman_clean___no_packages_nargs=0
_shtab_ahriman_clean___pacman_nargs=0
_shtab_ahriman_clean___no_pacman_nargs=0
_shtab_ahriman_repo_clean__h_nargs=0
_shtab_ahriman_repo_clean___help_nargs=0
_shtab_ahriman_repo_clean___cache_nargs=0
_shtab_ahriman_repo_clean___no_cache_nargs=0
_shtab_ahriman_repo_clean___chroot_nargs=0
_shtab_ahriman_repo_clean___no_chroot_nargs=0
_shtab_ahriman_repo_clean___manual_nargs=0
_shtab_ahriman_repo_clean___no_manual_nargs=0
_shtab_ahriman_repo_clean___packages_nargs=0
_shtab_ahriman_repo_clean___no_packages_nargs=0
_shtab_ahriman_repo_clean___pacman_nargs=0
_shtab_ahriman_repo_clean___no_pacman_nargs=0
_shtab_ahriman_service_config__h_nargs=0
_shtab_ahriman_service_config___help_nargs=0
_shtab_ahriman_service_config___secure_nargs=0
_shtab_ahriman_service_config___no_secure_nargs=0
_shtab_ahriman_config__h_nargs=0
_shtab_ahriman_config___help_nargs=0
_shtab_ahriman_config___secure_nargs=0
_shtab_ahriman_config___no_secure_nargs=0
_shtab_ahriman_repo_config__h_nargs=0
_shtab_ahriman_repo_config___help_nargs=0
_shtab_ahriman_repo_config___secure_nargs=0
_shtab_ahriman_repo_config___no_secure_nargs=0
_shtab_ahriman_service_config_validate__h_nargs=0
_shtab_ahriman_service_config_validate___help_nargs=0
_shtab_ahriman_service_config_validate__e_nargs=0
_shtab_ahriman_service_config_validate___exit_code_nargs=0
_shtab_ahriman_config_validate__h_nargs=0
_shtab_ahriman_config_validate___help_nargs=0
_shtab_ahriman_config_validate__e_nargs=0
_shtab_ahriman_config_validate___exit_code_nargs=0
_shtab_ahriman_repo_config_validate__h_nargs=0
_shtab_ahriman_repo_config_validate___help_nargs=0
_shtab_ahriman_repo_config_validate__e_nargs=0
_shtab_ahriman_repo_config_validate___exit_code_nargs=0
_shtab_ahriman_service_key_import__h_nargs=0
_shtab_ahriman_service_key_import___help_nargs=0
_shtab_ahriman_key_import__h_nargs=0
_shtab_ahriman_key_import___help_nargs=0
_shtab_ahriman_service_setup__h_nargs=0
_shtab_ahriman_service_setup___help_nargs=0
_shtab_ahriman_service_setup___generate_salt_nargs=0
_shtab_ahriman_service_setup___no_generate_salt_nargs=0
_shtab_ahriman_service_setup___makeflags_jobs_nargs=0
_shtab_ahriman_service_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_service_setup___multilib_nargs=0
_shtab_ahriman_service_setup___no_multilib_nargs=0
_shtab_ahriman_init__h_nargs=0
_shtab_ahriman_init___help_nargs=0
_shtab_ahriman_init___generate_salt_nargs=0
_shtab_ahriman_init___no_generate_salt_nargs=0
_shtab_ahriman_init___makeflags_jobs_nargs=0
_shtab_ahriman_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_init___multilib_nargs=0
_shtab_ahriman_init___no_multilib_nargs=0
_shtab_ahriman_repo_init__h_nargs=0
_shtab_ahriman_repo_init___help_nargs=0
_shtab_ahriman_repo_init___generate_salt_nargs=0
_shtab_ahriman_repo_init___no_generate_salt_nargs=0
_shtab_ahriman_repo_init___makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___multilib_nargs=0
_shtab_ahriman_repo_init___no_multilib_nargs=0
_shtab_ahriman_repo_setup__h_nargs=0
_shtab_ahriman_repo_setup___help_nargs=0
_shtab_ahriman_repo_setup___generate_salt_nargs=0
_shtab_ahriman_repo_setup___no_generate_salt_nargs=0
_shtab_ahriman_repo_setup___makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___multilib_nargs=0
_shtab_ahriman_repo_setup___no_multilib_nargs=0
_shtab_ahriman_setup__h_nargs=0
_shtab_ahriman_setup___help_nargs=0
_shtab_ahriman_setup___generate_salt_nargs=0
_shtab_ahriman_setup___no_generate_salt_nargs=0
_shtab_ahriman_setup___makeflags_jobs_nargs=0
_shtab_ahriman_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_setup___multilib_nargs=0
_shtab_ahriman_setup___no_multilib_nargs=0
_shtab_ahriman_service_shell__h_nargs=0
_shtab_ahriman_service_shell___help_nargs=0
_shtab_ahriman_service_shell__v_nargs=0
_shtab_ahriman_service_shell___verbose_nargs=0
_shtab_ahriman_shell__h_nargs=0
_shtab_ahriman_shell___help_nargs=0
_shtab_ahriman_shell__v_nargs=0
_shtab_ahriman_shell___verbose_nargs=0
_shtab_ahriman_user_add__h_nargs=0
_shtab_ahriman_user_add___help_nargs=0
_shtab_ahriman_user_list__h_nargs=0
_shtab_ahriman_user_list___help_nargs=0
_shtab_ahriman_user_list__e_nargs=0
_shtab_ahriman_user_list___exit_code_nargs=0
_shtab_ahriman_user_remove__h_nargs=0
_shtab_ahriman_user_remove___help_nargs=0
_shtab_ahriman_web__h_nargs=0
_shtab_ahriman_web___help_nargs=0
# $1=COMP_WORDS[1]
_shtab_compgen_files() {
compgen -f -- $1 # files
}
# $1=COMP_WORDS[1]
_shtab_compgen_dirs() {
compgen -d -- $1 # recurse into subdirs
}
# $1=COMP_WORDS[1]
_shtab_replace_nonword() {
echo "${1//[^[:word:]]/_}"
}
# set default values (called for the initial parser & any subparsers)
_set_parser_defaults() {
local subparsers_var="${prefix}_subparsers[@]"
sub_parsers=${!subparsers_var}
local current_option_strings_var="${prefix}_option_strings[@]"
current_option_strings=${!current_option_strings_var}
completed_positional_actions=0
_set_new_action "pos_${completed_positional_actions}" true
}
# $1=action identifier
# $2=positional action (bool)
# set all identifiers for an action's parameters
_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}"
local current_action_choices_var="${current_action}_choices[@]"
current_action_choices="${!current_action_choices_var}"
local current_action_nargs_var="${current_action}_nargs"
if [ -n "${!current_action_nargs_var}" ]; then
current_action_nargs="${!current_action_nargs_var}"
else
current_action_nargs=1
fi
current_action_args_start_index=$(( $word_index + 1 ))
current_action_is_positional=$2
}
# Notes:
# `COMPREPLY`: what will be rendered after completion is triggered
# `completing_word`: currently typed word to generate completions for
# `${!var}`: evaluates the content of `var` and expand its content as a variable
# hello="world"
# x="hello"
# ${!x} -> ${hello} -> "world"
_shtab_ahriman() {
local completing_word="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=()
prefix=_shtab_ahriman
word_index=0
_set_parser_defaults
word_index=1
# determine what arguments are appropriate for the current state
# of the arg parser
while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}"
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
# 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
_set_new_action $this_word false
fi
if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index >= \
$current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true
fi
let "word_index+=1"
done
# Generate the completions
if [[ "${completing_word}" == -* ]]; then
# optional argument started: use option strings
COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
else
# use choices & compgen
local IFS=$'\n' # items may contain spaces, so delimit using newline
COMPREPLY=( $([ -n "${current_action_compgen}" ] \
&& "${current_action_compgen}" "${completing_word}") )
unset IFS
COMPREPLY+=( $(compgen -W "${current_action_choices[*]}" -- "${completing_word}") )
fi
return 0
}
complete -o filenames -F _shtab_ahriman ahriman

View File

@ -0,0 +1,666 @@
#compdef ahriman
# AUTOMATICALLY GENERATED by `shtab`
_shtab_ahriman_commands() {
local _commands=(
"add:add existing or new package to the build queue"
"aur-search:search for package in AUR using API"
"check:check for packages updates. Same as repo-update --dry-run --no-manual"
"clean:remove local caches"
"config:dump configuration for the specified architecture"
"config-validate:validate configuration and print found errors"
"daemon:start process which periodically will run update process"
"help:show help message for application or command and exit"
"help-commands-unsafe:list unsafe commands as defined in default args"
"help-updates:request AUR for current version and compare with current service version"
"help-version:print application and its dependencies versions"
"init:create initial service configuration, requires root"
"key-import:import PGP key from public sources to the repository user"
"package-add:add existing or new package to the build queue"
"package-remove:remove package from the repository"
"package-status:request status of the package"
"package-status-remove:remove the package from the status page"
"package-status-update:update package status on the status page"
"package-update:add existing or new package to the build queue"
"patch-add:create or update patched PKGBUILD function or variable"
"patch-list:list available patches for the package"
"patch-remove:remove patches for the package"
"patch-set-add:create or update source patches"
"rebuild:force rebuild whole repository"
"remove:remove package from the repository"
"remove-unknown:remove packages which are missing in AUR and do not have local PKGBUILDs"
"repo-backup:backup repository settings and database"
"repo-check:check for packages updates. Same as repo-update --dry-run --no-manual"
"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"
"repo-remove-unknown:remove packages which are missing in AUR and do not have local PKGBUILDs"
"repo-report:generate repository report according to current settings"
"repo-restore:restore settings and database"
"repo-setup:create initial service configuration, requires root"
"repo-sign:(re-)sign packages and repository database according to current settings"
"repo-status-update:update repository status on the status page"
"repo-sync:sync repository files to remote server according to current settings"
"repo-tree:dump repository tree based on packages dependencies"
"repo-triggers:run triggers on empty build result as configured by settings"
"repo-update:check for packages updates and run build process if requested"
"report:generate repository report according to current settings"
"search:search for package in AUR using API"
"service-clean:remove local caches"
"service-config:dump configuration for the specified architecture"
"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"
"setup:create initial service configuration, requires root"
"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"
"sync:sync repository files to remote server according to current settings"
"update:check for packages updates and run build process if requested"
"user-add:update user for web services with the given password and role. In case if password was not entered it will be asked interactively"
"user-list:list users from the user mapping and their roles"
"user-remove:remove user from the user mapping and update the configuration"
"version:print application and its dependencies versions"
"web:start web server"
)
_describe 'ahriman commands' _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:"
"--log-handler[explicit log handler specification. If none set, the handler will be guessed from environment]:log_handler:(console syslog journald)"
{--report,--no-report}"[force enable or disable reporting to web service]:report:"
{-q,--quiet}"[force disable any logging]"
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable]"
"(- : *)"{-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]: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)"
{-u,--username}"[build as user]: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]: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)"
"(*):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]: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:"
)
_shtab_ahriman_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches]:cache:"
{--chroot,--no-chroot}"[clear build chroot]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue]:manual:"
{--packages,--no-packages}"[clear directory with built packages]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache]:pacman:"
)
_shtab_ahriman_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--secure,--no-secure}"[hide passwords and secrets from output]: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]"
)
_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]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies]:dependencies:"
{--local,--no-local}"[enable or disable checking of local packages for updates]:local:"
{--manual,--no-manual}"[include or exclude manual updates]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
)
_shtab_ahriman_help_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":show help message for specific command:"
)
_shtab_ahriman_help_commands_unsafe_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise:"
)
_shtab_ahriman_help_updates_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit code if updates available]"
)
_shtab_ahriman_help_version_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_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:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository]: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:"
)
_shtab_ahriman_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import]: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]: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)"
{-u,--username}"[build as user]:username:"
"(*):package source (base name, path to local files, remote URL):"
)
_shtab_ahriman_package_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:"
)
_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]:info:"
{-s,--status}"[filter packages by status]:status:(unknown pending building failed success)"
"(*)::filter status by package base:"
)
_shtab_ahriman_package_status_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):remove specified packages from status page:"
)
_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:"
)
_shtab_ahriman_package_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--dependencies,--no-dependencies}"[process missing package dependencies]: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)"
{-u,--username}"[build as user]:username:"
"(*):package source (base name, path to local files, remote URL):"
)
_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:"
)
_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:"
)
_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:"
":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:"
":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]"
{-s,--status}"[filter packages by status. Requires --from-database to be set]:status:(unknown pending building failed success)"
{-u,--username}"[build as user]:username:"
)
_shtab_ahriman_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:"
)
_shtab_ahriman_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal]"
)
_shtab_ahriman_repo_backup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":path of the output archive:"
)
_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]: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:"
)
_shtab_ahriman_repo_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches]:cache:"
{--chroot,--no-chroot}"[clear build chroot]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue]:manual:"
{--packages,--no-packages}"[clear directory with built packages]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache]:pacman:"
)
_shtab_ahriman_repo_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--secure,--no-secure}"[hide passwords and secrets from output]: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]"
)
_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]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies]:dependencies:"
{--local,--no-local}"[enable or disable checking of local packages for updates]:local:"
{--manual,--no-manual}"[include or exclude manual updates]:manual:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
)
_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:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository]: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:"
)
_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]"
{-s,--status}"[filter packages by status. Requires --from-database to be set]:status:(unknown pending building failed success)"
{-u,--username}"[build as user]:username:"
)
_shtab_ahriman_repo_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal]"
)
_shtab_ahriman_repo_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_restore_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-o,--output}"[root path of the extracted files]: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:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository]: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:"
)
_shtab_ahriman_repo_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages:"
)
_shtab_ahriman_repo_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new status]:status:(unknown pending building failed success)"
)
_shtab_ahriman_repo_sync_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_tree_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_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:"
)
_shtab_ahriman_repo_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies]: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]:local:"
{--manual,--no-manual}"[include or exclude manual updates]:manual:"
{-u,--username}"[build as user]:username:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages]: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:"
)
_shtab_ahriman_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_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]: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)"
"(*):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]:cache:"
{--chroot,--no-chroot}"[clear build chroot]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue]:manual:"
{--packages,--no-packages}"[clear directory with built packages]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache]:pacman:"
)
_shtab_ahriman_service_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--secure,--no-secure}"[hide passwords and secrets from output]: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]"
)
_shtab_ahriman_service_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import]: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:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository]: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:"
)
_shtab_ahriman_service_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":instead of dropping into shell, just execute the specified code:"
)
_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:"
{--generate-salt,--no-generate-salt}"[generate salt for user passwords]:generate_salt:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores]:makeflags_jobs:"
"--mirror[use the specified explicitly mirror instead of including mirrorlist]:mirror:"
{--multilib,--no-multilib}"[add or do not multilib repository]: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:"
)
_shtab_ahriman_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":instead of dropping into shell, just execute the specified code:"
)
_shtab_ahriman_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages:"
)
_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]:info:"
{-s,--status}"[filter packages by status]:status:(unknown pending building failed success)"
"(*)::filter status by package base:"
)
_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:"
)
_shtab_ahriman_sync_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates]:aur:"
{--dependencies,--no-dependencies}"[process missing package dependencies]: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]:local:"
{--manual,--no-manual}"[include or exclude manual updates]:manual:"
{-u,--username}"[build as user]:username:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages]: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:"
)
_shtab_ahriman_user_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--key[optional PGP key used by this user. The private key must be imported]:key:"
"--packager[optional packager id used for build process in form of \`Name Surname \<mail\@example.com\>\`]:packager:"
{-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)"
":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:"
)
_shtab_ahriman_user_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":username for web service:"
)
_shtab_ahriman_version_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_web_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman() {
local context state line curcontext="$curcontext" one_or_more='(-)*' remainder='(*)'
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
case $state in
ahriman)
words=($line[1] "${words[@]}")
(( 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-create-keyring) _arguments -C $_shtab_ahriman_repo_create_keyring_options ;;
repo-create-mirrorlist) _arguments -C $_shtab_ahriman_repo_create_mirrorlist_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 ;;
esac
esac
}
typeset -A opt_args
_shtab_ahriman "$@"

View File

@ -14,7 +14,6 @@ import os
import sys
from pathlib import Path
from unittest import mock
from ahriman.version import __version__
@ -24,28 +23,11 @@ sys.path.insert(0, str(basedir))
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
for module in (
"aioauth_client",
"aiohttp",
"aiohttp.web",
"aiohttp.web_exceptions",
"aiohttp.web_response",
"aiohttp.web_urldispatcher",
"aiohttp_jinja2",
"aiohttp_security",
"aiohttp_session",
"aiohttp_session.cookie_storage",
"boto3",
"cryptography",
"pyalpm",
):
sys.modules[module] = mock.Mock()
# -- Project information -----------------------------------------------------
project = "ahriman"
copyright = "2021-2022, ahriman team"
copyright = "2021-2023, ahriman team"
author = "ahriman team"
# The full version, including alpha/beta/rc tags
@ -89,7 +71,7 @@ html_theme = "default" if on_rtd else "alabaster"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_static_path = []
add_module_names = False
@ -102,6 +84,8 @@ autoclass_content = "both"
autodoc_member_order = "groupwise"
autodoc_mock_imports = ["pyalpm"]
autodoc_default_options = {
"no-undoc-members": True,
}

View File

@ -12,6 +12,14 @@ There are two variable types which have been added to default ones, they are pat
Path values, except for casting to ``pathlib.Path`` type, will be also expanded to absolute paths relative to the configuration path. E.g. if path is set to ``ahriman.ini.d/logging.ini`` and root configuration path is ``/etc/ahriman.ini``, the value will be expanded to ``/etc/ahriman.ini.d/logging.ini``. In order to disable path expand, use the full path, e.g. ``/etc/ahriman.ini.d/logging.ini``.
There is also additional subcommand which will allow to validate configuration and print found errors. In order to do so, run ``service-config-validate`` subcommand, e.g.:
.. code-block:: shell
ahriman -a x86_64 service-config-validate
It will check current settings on common errors and compare configuration with known schema.
``settings`` group
------------------
@ -20,11 +28,12 @@ Base configuration settings.
* ``include`` - path to directory with configuration files overrides, string, required.
* ``database`` - path to SQLite database, string, required.
* ``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.
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.
* ``database`` - path to pacman system database cache, string, required.
* ``mirror`` - package database mirror used by pacman for syncronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option.
@ -41,10 +50,11 @@ Base authorization settings. ``OAuth`` provider requires ``aioauth-client`` libr
* ``allow_read_only`` - allow requesting status APIs without authorization, boolean, required.
* ``client_id`` - OAuth2 application client ID, string, required in case if ``oauth`` is used.
* ``client_secret`` - OAuth2 application client secret key, string, required in case if ``oauth`` is used.
* ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 url-safe base64-encoded bytes and can be generated as following ``base64.urlsafe_b64encode(os.urandom(32)).decode("utf8")``. If not set, it will be generated automatically; note, however, that in this case, all sessions will be automatically expired during restart.
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service, integer, optional, default is 7 days.
* ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used.
* ``oauth_scopes`` - scopes list for OAuth2 provider, which will allow retrieving user email (which is used for checking user permissions), e.g. ``https://www.googleapis.com/auth/userinfo.email`` for ``GoogleClient`` or ``user:email`` for ``GithubClient``, space separated list of strings, required in case if ``oauth`` is used.
* ``salt`` - password hash salt, string, required in case if authorization enabled (automatically generated by ``create-user`` subcommand).
* ``salt`` - password hash salt, string, required in case if authorization enabled (automatically generated by ``user-add`` subcommand).
Authorized users are stored inside internal database, if any of external provides are used the password field for non-service users must be empty.
@ -59,6 +69,8 @@ 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
--------------------
@ -75,14 +87,72 @@ 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
----------------
Web server settings. If any of ``host``/``port`` is not set, web integration will be disabled. Group name can refer to architecture, e.g. ``web:x86_64`` can be used for x86_64 architecture specific settings. This feature requires ``aiohttp`` libraries to be installed.
* ``address`` - optional address in form ``proto://host:port`` (``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used.
* ``debug`` - enable debug toolbar, boolean, optional, default ``no``.
* ``debug_check_host`` - check hosts to access debug toolbar, boolean, optional, default ``no``.
* ``debug_allowed_hosts`` - allowed hosts to get access to debug toolbar, space separated list of string, optional.
* ``host`` - host to bind, string, optional.
* ``index_url`` - full url of the repository index page, string, optional.
* ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.
* ``port`` - port to bind, int, optional.
* ``static_path`` - path to directory with static files, string, required.
* ``templates`` - path to templates directory, string, required.
* ``unix_socket`` - path to the listening unix socket, string, optional. If set, server will create the socket on the specified address which can (and will) be used by application. Note, that unlike usual host/port configuration, unix socket allows to perform requests without authorization.
* ``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
---------------------
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.:
* by SSH key: generate SSH key as ``ahriman`` user and put public part of it to the repository keys.
* by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`_.
Available options are:
* ``target`` - list of remote pull triggers to be used, space separated list of strings, optional, defaults to ``gitremote``. It must point to valid section (or to section with architecture), e.g. ``gitremote`` must point to either ``gitremote`` or ``gitremote:x86_64`` (the one with architecture has higher priority).
@ -97,13 +167,19 @@ Remote pull trigger
Remote git source synchronization settings. Same as remote pull triggers those triggers are used for PKGBUILD synchronization - push updated PKGBUILDs to the remote repository after build process.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.:
* by SSH key: generate SSH key as ``ahriman`` user and put public part of it to the repository keys.
* by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`_.
Available options are:
* ``target`` - list of remote push triggers to be used, space separated list of strings, optional, defaults to ``gitremote``. It must point to valid section (or to section with architecture), e.g. ``gitremote`` must point to either ``gitremote`` or ``gitremote:x86_64`` (the one with architecture has higher priority).
Remote push trigger
^^^^^^^^^^^^^^^^^^^
* ``commit_author`` - git commit author, string, optional. In case if not set, the git will generate author for you. Note, however, that in this case it will disclosure your hostname.
* ``push_url`` - url of the remote repository to which PKGBUILDs should be pushed after build process, string, required.
* ``push_branch`` - branch of the remote repository to which PKGBUILDs should be pushed after build process, string, optional, default is ``master``.
@ -114,7 +190,7 @@ Report generation settings.
* ``target`` - list of reports to be generated, space separated list of strings, required. It must point to valid section (or to section with architecture), e.g. ``somerandomname`` must point to existing section, ``email`` must point to either ``email`` or ``email:x86_64`` (the one with architecture has higher priority).
Type will be read from several ways:
Type will be read from several sources:
* In case if ``type`` option set inside the section, it will be used.
* Otherwise, it will look for type from section name removing architecture name.
@ -179,7 +255,7 @@ Remote synchronization settings.
* ``target`` - list of synchronizations to be used, space separated list of strings, required. It must point to valid section (or to section with architecture), e.g. ``somerandomname`` must point to existing section, ``github`` must point to one of ``github`` of ``github:x86_64`` (with architecture it has higher priority).
Type will be read from several ways:
Type will be read from several sources:
* In case if ``type`` option set inside the section, it will be used.
* Otherwise, it will look for type from section name removing architecture name.
@ -222,21 +298,4 @@ Requires ``boto3`` library to be installed. Section name must be either ``s3`` (
* ``bucket`` - bucket name (e.g. ``bucket``), string, required.
* ``chunk_size`` - chunk size for calculating entity tags, int, optional, default 8 * 1024 * 1024.
* ``region`` - bucket region (e.g. ``eu-central-1``), string, required.
* ``secret_key`` - AWS secret access key, string, required.
``web:*`` groups
----------------
Web server settings. If any of ``host``/``port`` is not set, web integration will be disabled. Group name can refer to architecture, e.g. ``web:x86_64`` can be used for x86_64 architecture specific settings. This feature requires ``aiohttp`` libraries to be installed.
* ``address`` - optional address in form ``proto://host:port`` (``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used.
* ``debug`` - enable debug toolbar, boolean, optional, default ``no``.
* ``debug_check_host`` - check hosts to access debug toolbar, boolean, optional, default ``no``.
* ``debug_allowed_hosts`` - allowed hosts to get access to debug toolbar, space separated list of string, optional.
* ``host`` - host to bind, string, optional.
* ``index_url`` - full url of the repository index page, string, optional.
* ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.
* ``port`` - port to bind, int, optional.
* ``static_path`` - path to directory with static files, string, required.
* ``templates`` - path to templates directory, string, required.
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.
* ``secret_key`` - AWS secret access key, string, required.

View File

@ -1,4 +1,3 @@
FAQ
===
@ -18,7 +17,7 @@ TL;DR
.. code-block:: shell
yay -S ahriman
sudo ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "repository"
ahriman -a x86_64 service-setup --packager "ahriman bot <ahriman@example.com>" --repository "repository"
systemctl enable --now ahriman@x86_64.timer
Long answer
@ -26,6 +25,33 @@ Long answer
The idea is to install the package as usual, create working directory tree, create configuration for ``sudo`` and ``devtools``. Detailed description of the setup instruction can be found :doc:`here <setup>`.
How to validate settings
^^^^^^^^^^^^^^^^^^^^^^^^
There is special command which can be used in order to validate current configuration:
.. code-block:: shell
ahriman -a x86_64 service-config-validate --exit-code
This command will print found errors, based on `cerberus <https://docs.python-cerberus.org/>`_, e.g.:
.. code-block:: shell
auth
ssalt: unknown field
target: none or more than one rule validate
oneof definition 0: unallowed value mapping
oneof definition 1: field 'salt' is required
oneof definition 2: unallowed value mapping
oneof definition 2: field 'salt' is required
oneof definition 2: field 'client_id' is required
oneof definition 2: field 'client_secret' is required
gitremote
pull_url: unknown field
If an additional flag ``--exit-code`` is supplied, the application will return non-zero exit code, which can be used partially in scripts.
What does "architecture specific" mean / How to configure for different architectures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -117,65 +143,60 @@ Before using this command you will need to create local directory, put ``PKGBUIL
How to fetch PKGBUILDs from remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will need:
For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will need to configure trigger as following:
#.
Append ``triggers`` option in ``build`` section with the following line:
.. code-block:: ini
.. code-block:: ini
[remote-pull]
target = gitremote
[build]
triggers = ahriman.core.gitremote.RemotePullTrigger
#.
Configure trigger as following:
.. code-block:: ini
[remote-pull]
target = gitremote
[gitremote]
pull_url = https://github.com/username/repository
[gitremote]
pull_url = https://github.com/username/repository
During the next application run it will fetch repository from the specified url and will try to find packages there which can be used as local sources.
How to push updated PKGBUILDs to remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For that purpose you'd need to use another trigger called ``RemotePushTrigger``. Configure it as following:
For that purpose you'd need to use another trigger called ``RemotePushTrigger``. Configure trigger as following:
#.
Append ``triggers`` option in ``build`` section with the trigger name:
.. code-block:: ini
.. code-block:: ini
[remote-push]
target = gitremote
[build]
triggers = ahriman.core.gitremote.RemotePushTrigger
#.
Configure trigger as following:
.. code-block:: ini
[remote-push]
target = gitremote
[gitremote]
push_url = https://github.com/username/repository
[gitremote]
push_url = https://github.com/username/repository
Unlike ``RemotePullTrigger`` trigger, the ``RemotePushTrigger`` more likely will require authorization. It is highly recommended to use application tokens for that instead of using your password (e.g. for Github you can generate tokens `here <https://github.com/settings/tokens>`_ with scope ``public_repo``). Authorization can be supplied by using authorization part of the url, e.g. ``https://key:token@github.com/username/repository``.
How to change PKGBUILDs before build
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Well it is supported also. The recommended way is to patch specific function, e.g. by running ``sudo -u ahriman ahriman patch-add ahriman version``. This command will prompt for new value of the PKGBUILD variable ``version``. You can also write it to file and read from it ``sudo -u ahriman ahriman patch-add ahriman version version.patch``.
Well it is supported also. The recommended way is to patch specific function, e.g. by running
.. code-block:: shell
sudo -u ahriman ahriman patch-add ahriman version
This command will prompt for new value of the PKGBUILD variable ``version``. You can also write it to file and read from it:
.. code-block:: shell
sudo -u ahriman ahriman patch-add ahriman version version.patch
Alternatively you can create full-diff patches, which are calculated by using ``git diff`` from current PKGBUILD master branch:
#. Clone sources from AUR.
#. Make changes you would like to (e.g. edit ``PKGBUILD``, add external patches).
#. Run ``sudo -u ahriman ahriman patch-set-add /path/to/local/directory/with/PKGBUILD``.
#.
Clone sources from AUR.
#.
Make changes you would like to (e.g. edit ``PKGBUILD``, add external patches).
#.
Run command
.. code-block:: shell
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).
@ -197,7 +218,7 @@ TL;DR
.. code-block:: shell
sudo -u ahriman ahriman key-import ...
sudo -u ahriman ahriman service-key-import ...
How to update VCS packages
^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -219,6 +240,62 @@ Also, there is command ``repo-remove-unknown`` which checks packages in AUR and
Remove commands also remove any package files (patches, caches etc).
How to sign repository
^^^^^^^^^^^^^^^^^^^^^^
Repository sign feature is available in several configurations. The recommended way is just to sign repository database file by single key instead of trying to sign each package. However, the steps are pretty same, just configuration is a bit differ. For more details about options kindly refer to :doc:`configuration reference <configuration>`.
#.
First you would need to create the key on your local machine:
.. code-block:: shell
gpg --full-generate-key
This command will prompt you for several questions. Most of them may be left default, but you will need to fill real name and email address with some data. Because at the moment the service doesn't support passphrases, it must be left blank.
#.
The command above will generate key and print its hash, something like ``8BE91E5A773FB48AC05CC1EDBED105AED6246B39``. Copy it.
#.
Export your private key by using the hash above:
.. code-block:: shell
gpg --export-secret-keys -a 8BE91E5A773FB48AC05CC1EDBED105AED6246B39 > repository-key.gpg
#.
Copy the specified key to the build machine (i.e. where the service is running).
#.
Import the specified key to the service user:
.. code-block:: shell
sudo -u ahriman gpg --import repository-key.gpg
Don't forget to remove the key from filesystem after import.
#.
Change trust level to ``ultimate``:
.. code-block:: shell
sudo -u ahriman gpg --edit-key 8BE91E5A773FB48AC05CC1EDBED105AED6246B39
The command above will drop you into gpg shell, in which you will need to type ``trust``, choose ``5 = I trust ultimately``, confirm and exit ``quit``.
#.
Proceed with service configuration according to the :doc:`configuration <configuration>`:
.. code-block:: ini
[sign]
target = repository
key = 8BE91E5A773FB48AC05CC1EDBED105AED6246B39
How to rebuild packages after library update
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -290,17 +367,17 @@ The default action (in case if no arguments provided) is ``repo-update``. Basica
docker run --privileged -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
``--privileged`` flag is required to make mount possible inside container. In addition, you can pass own configuration overrides by using the same ``-v`` flag, e.g.:
``--privileged`` flag is required to make mount possible inside container. In order to make data available outside of container, you would need to mount local (parent) directory inside container by using ``-v /path/to/local/repo:/var/lib/ahriman`` argument, where ``/path/to/local/repo`` is a path to repository on local machine. In addition, you can pass own configuration overrides by using the same ``-v`` flag, e.g.:
.. code-block:: shell
docker run -v /path/to/local/repo:/var/lib/ahriman -v /etc/ahriman.ini:/etc/ahriman.ini.d/10-overrides.ini arcan1s/ahriman:latest
docker run --privileged -v /path/to/local/repo:/var/lib/ahriman -v /path/to/overrides/overrides.ini:/etc/ahriman.ini.d/10-overrides.ini arcan1s/ahriman:latest
The action can be specified during run, e.g.:
.. code-block:: shell
docker run arcan1s/ahriman:latest package-add ahriman --now
docker run --privileged -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest package-add ahriman --now
For more details please refer to docker FAQ.
@ -313,18 +390,33 @@ The following environment variables are supported:
* ``AHRIMAN_DEBUG`` - if set all commands will be logged to console.
* ``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_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_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`` 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.
* ``AHRIMAN_REPOSITORY`` - repository name, default is ``aur-clone``.
* ``AHRIMAN_REPOSITORY_ROOT`` - repository root. Because of filesystem rights it is required to override default repository root. By default, it uses ``ahriman`` directory inside ahriman's home, which can be passed as mount volume.
* ``AHRIMAN_USER`` - ahriman user, usually must not be overwritten, default is ``ahriman``.
* ``AHRIMAN_UNIX_SOCKET`` - full path to unix socket which is used by web server, default is empty. Note that more likely you would like to put it inside ``AHRIMAN_REPOSITORY_ROOT`` directory (e.g. ``/var/lib/ahriman/ahriman/ahriman-web.sock``) or to ``/tmp``.
* ``AHRIMAN_USER`` - ahriman user, usually must not be overwritten, default is ``ahriman``.
* ``AHRIMAN_VALIDATE_CONFIGURATION`` - if set validate service configuration
You can pass any of these variables by using ``-e`` argument, e.g.:
.. code-block:: shell
docker run -e AHRIMAN_PORT=8080 arcan1s/ahriman:latest
docker run --privileged -e AHRIMAN_PORT=8080 -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Daemon service
^^^^^^^^^^^^^^
There is special ``repo-daemon`` subcommand which emulates systemd timer and will perform repository update periodically:
.. code-block:: shell
docker run --privileged -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest repo-daemon
This command uses same rules as ``repo-update``, thus, e.g. requires ``--privileged`` flag.
Web service setup
^^^^^^^^^^^^^^^^^
@ -333,26 +425,119 @@ Well for that you would need to have web container instance running forever; it
.. code-block:: shell
docker run -p 8080:8080 -e AHRIMAN_PORT=8080 -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
docker run --privileged -p 8080:8080 -e AHRIMAN_PORT=8080 -e AHRIMAN_UNIX_SOCKET=/var/lib/ahriman/ahriman/ahriman-web.sock -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Note about ``AHRIMAN_PORT`` environment variable which is required in order to enable web service. An additional port bind by ``-p 8080:8080`` is required to pass docker port outside of container.
For every next container run use arguments ``-e AHRIMAN_PORT=8080 --net=host``, e.g.:
The ``AHRIMAN_UNIX_SOCKET`` variable is not required, however, highly recommended as it can be used for interprocess communications. If you set this variable you would like to be sure that this path is available outside of container if you are going to use multiple docker instances.
If you are using ``AHRIMAN_UNIX_SOCKET`` variable, for every next container run it has to be passed also, e.g.:
.. code-block:: shell
docker run --privileged -e AHRIMAN_PORT=8080 --net=host -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
docker run --privileged -e AHRIMAN_UNIX_SOCKET=/var/lib/ahriman/ahriman/ahriman-web.sock -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Daemon service
^^^^^^^^^^^^^^
There is special subcommand which emulates systemd timer and will perform repository update periodically:
Otherwise, you would need to pass ``AHRIMAN_PORT`` and mount container network to the host system (``--net=host``), e.g.:
.. code-block:: shell
docker run arcan1s/ahriman:latest daemon
docker run --privileged --net=host -e AHRIMAN_PORT=8080 -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
This command uses same rules as ``repo-update``, thus, e.g. requires ``--privileged`` flag.
Non-x86_64 architecture setup
-----------------------------
The following section describes how to setup ahriman with architecture different from x86_64, as example i686. For most cases you have base repository available, e.g. archlinux32 repositories for i686 architecture; in case if base repository is not available, steps are a bit different, however, idea remains the same.
Physical server setup
^^^^^^^^^^^^^^^^^^^^^
In this example we are going to use files and packages which are provided by official repositories of the used architecture. Note, that versions might be different, thus you need to find correct versions on the distribution web site, e.g. `archlinux32 packages <https://www.archlinux32.org/packages/>`_.
#.
First, considering having base Arch Linux system, we need to install keyring for the specified repositories:
.. code-block:: shell
wget http://pool.mirror.archlinux32.org/i686/core/archlinux32-keyring-20220927-1.0-any.pkg.tar.zst
pacman -U archlinux32-keyring-20220927-1.0-any.pkg.tar.zst
#.
In order to run ``devtools`` scripts for custom architecture they also need specific ``makepkg`` configuration, it can be retrieved by installing the ``devtools`` package of the distribution:
.. code-block:: shell
wget http://pool.mirror.archlinux32.org/i686/extra/devtools-20221208-1.0-any.pkg.tar.zst
pacman -U devtools-20221208-1.0-any.pkg.tar.zst
Alternatively, you can create your own ``makepkg`` configuration and save it as ``/usr/share/devtools/makepkg-i686.conf``.
#.
Setup repository as usual:
.. code-block:: shell
ahriman -a i686 service-setup --mirror 'http://de.mirror.archlinux32.org/$arch/$repo'--no-multilib ...
In addition to usual options, you need to specify the following options:
* ``--mirror`` - link to the mirrors which will be used instead of official repositories.
* ``--no-multilib`` - in the example we are using i686 architecture for which multilib repository doesn't exist.
Docker container setup
^^^^^^^^^^^^^^^^^^^^^^
There are two possible ways to achieve same setup, by using docker container. The first one is just mount required files inside container and run it as usual (with specific environment variables). Another one is to create own container based on official one:
#.
Clone official container as base:
.. code-block:: dockerfile
FROM arcan1s/ahriman:latest
#.
Init pacman keys. This command is required in order to populate distribution keys:
.. code-block:: dockerfile
RUN pacman-key --init
#.
Install packages as it was described above:
.. code-block:: dockerfile
RUN pacman --noconfirm -Sy wget
RUN wget http://pool.mirror.archlinux32.org/i686/extra/devtools-20221208-1.0-any.pkg.tar.zst && pacman --noconfirm -U devtools-20221208-1.0-any.pkg.tar.zst
RUN wget http://pool.mirror.archlinux32.org/i686/core/archlinux32-keyring-20220927-1.0-any.pkg.tar.zst && pacman --noconfirm -U archlinux32-keyring-20220927-1.0-any.pkg.tar.zst
#.
At that point you should have full ``Dockerfile`` like:
.. code-block:: dockerfile
FROM arcan1s/ahriman:latest
RUN pacman-key --init
RUN pacman --noconfirm -Sy wget
RUN wget http://pool.mirror.archlinux32.org/i686/extra/devtools-20221208-1.0-any.pkg.tar.zst && pacman --noconfirm -U devtools-20221208-1.0-any.pkg.tar.zst
RUN wget http://pool.mirror.archlinux32.org/i686/core/archlinux32-keyring-20220927-1.0-any.pkg.tar.zst && pacman --noconfirm -U archlinux32-keyring-20220927-1.0-any.pkg.tar.zst
#.
After that you can build you own container, e.g.:
.. code-block:: shell
docker build --tag ahriman-i686:latest
#.
Now you can run locally built container as usual with passing environment variables for setup command:
.. code-block:: shell
docker run --privileged -p 8080:8080 -e AHRIMAN_ARCHITECTURE=i686 -e AHRIMAN_PACMAN_MIRROR='http://de.mirror.archlinux32.org/$arch/$repo' -e AHRIMAN_MULTILIB= ahriman-i686:latest
Remote synchronization
----------------------
@ -478,7 +663,7 @@ How to report by email
.. code-block:: shell
yay -S python-jinja
yay -S --asdeps python-jinja
#.
Configure the service:
@ -505,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:
@ -529,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>`_.
@ -568,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
-----------
@ -579,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:
@ -600,29 +859,52 @@ 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
#.
Create user for the service:
#.
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):
.. code-block:: ini
[web]
unix_socket = /var/lib/ahriman/ahriman-web.sock
This socket path must be available for web service instance and must be available for application instances (e.g. in case if you are using docker container, see above, you need to be sure that the socket is passed to the root filesystem).
By the way, unix socket variable will be automatically set in case if ``--web-unix-socket`` argument is supplied to the ``setup`` subcommand.
Alternatively, you need to create user for the service:
.. code-block:: shell
sudo -u ahriman ahriman user-add --as-service -r write api
sudo -u ahriman ahriman user-add -r full api
This command will ask for the password, just type it in stdin; *do not* leave the field blank, user will not be able to authorize.
This command will ask for the password, just type it in stdin; *do not* leave the field blank, user will not be able to authorize, and finally configure the application:
#.
Create end-user ``sudo -u ahriman ahriman user-add -r write my-first-user`` with password.
.. code-block:: ini
#. Restart web service ``systemctl restart ahriman-web@x86_64``.
[web]
username = api
password = pa55w0rd
#.
Create end-user with password:
.. code-block:: shell
sudo -u ahriman ahriman user-add -r full my-first-user
#.
Restart web service ``systemctl restart ahriman-web@x86_64``.
How to enable OAuth authorization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -634,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:
@ -652,16 +934,28 @@ 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
sudo -u ahriman ahriman user-add --as-service -r write api
sudo -u ahriman ahriman user-add --as-service -r full api
#.
Create end-user ``sudo -u ahriman ahriman user-add -r write my-first-user``. When it will ask for the password leave it blank.
Create end-user:
#. Restart web service ``systemctl restart ahriman-web@x86_64``.
.. code-block:: shell
sudo -u ahriman ahriman user-add -r full my-first-user
When it will ask for the password leave it blank.
#.
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
------------------
@ -673,7 +967,7 @@ The service provides several commands aim to do easy repository backup and resto
.. code-block:: shell
sudo ahriman repo-backup /tmp/repo.tar.gz
ahriman repo-backup /tmp/repo.tar.gz
This command will pack all configuration files together with database file into the archive specified as command line argument (i.e. ``/tmp/repo.tar.gz``). In addition it will also archive ``cache`` directory (the one which contains local clones used by e.g. local packages) and ``.gnupg`` of the ``ahriman`` user.
@ -681,14 +975,14 @@ The service provides several commands aim to do easy repository backup and resto
Copy created archive from source server ``server1.example.com`` to target ``server2.example.com``.
#.
Install ahriman as usual on the target server ``server2.example.com`` if you didn't yet.
Install package as usual on the target server ``server2.example.com`` if you didn't yet.
#.
Extract archive e.g. by using subcommand:
.. code-block:: shell
sudo ahriman repo-restore /tmp/repo.tar.gz
ahriman repo-restore /tmp/repo.tar.gz
An additional argument ``-o``/``--output`` can be used to specify extraction root (``/`` by default).
@ -734,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>`_
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@ -755,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,10 +12,17 @@ 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. `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>`_.
Contents
--------

View File

@ -10,9 +10,9 @@ Initial setup
.. code-block:: shell
sudo ahriman -a x86_64 repo-setup ...
sudo ahriman -a x86_64 service-setup ...
``repo-setup`` literally does the following steps:
``service-setup`` literally does the following steps:
#.
Create ``/var/lib/ahriman/.makepkg.conf`` with ``makepkg.conf`` overrides if required (at least you might want to set ``PACKAGER``):
@ -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.
@ -88,4 +88,6 @@ Initial setup
.. code-block:: shell
sudo -u ahriman ahriman -a x86_64 package-add ahriman --now
sudo -u ahriman ahriman -a x86_64 package-add ahriman --now --refresh
The ``--refresh`` flag is required in order to handle local database update.

View File

@ -19,7 +19,7 @@ For the configuration details and settings explanation kindly refer to the :doc:
This trigger will be called before any action (``on_start``) and pulls remote PKGBUILD repository locally; after that it copies found PKGBUILDs from the cloned repository to the local cache. It is useful in case if you have patched PGKBUILDs (or even missing in AUR) which you would like to use for package building and, technically, just simplifies the local package building.
In order to update those packages you would need to clone your repository separately, make changes in PKGBUILD (e.g. bump version and update checksums), commit them and push back. On the next ahriman's repository update, it will pull changes you commited and will perform package update.
In order to update those packages you would need to clone your repository separately, make changes in PKGBUILD (e.g. bump version and update checksums), commit them and push back. On the next ahriman's repository update, it will pull changes you committed and will perform package update.
``ahriman.core.gitremote.RemotePushTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -31,15 +31,59 @@ 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``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This trigger takes build result (``on_result``) and performs syncing of the local packages to the remote mirror (e.g. S3 or just by rsync).
Context variables
-----------------
By default, only configuration and architecture are passed to triggers. However, some triggers might want to have access to other high-level wrappers. In order to provide such ability and avoid (double) initialization, the service provides a global context variables, which can be accessed from ``ahriman.core`` package:
.. code-block:: python
from ahriman.core import context
ctx = context.get()
Just because context is wrapped inside ``contexvars.ContextVar``, you need to explicitly extract variable by ``get()`` method. Later you can extract any variable if it is set, e.g.:
.. code-block:: python
from ahriman.core.database import SQLite
from ahriman.models.context_key import ContextKey
database = ctx.get(ContextKey("database", SQLite))
In order to provide typed API, all variables are stored together with their type. The ``get(ContextKey)`` method will throw ``KeyError`` in case if key is missing. Alternatively you can set your own variable inside context:
.. code-block:: python
ctx.set(ContextKey("answer", int), 42)
context.set(ctx)
Note, however, that there are several limitations:
* Context variables are immutable, thus you cannot override value if the key already presented.
* The ``return_type`` of ``ContextKey`` should match the value type, otherwise exception will be thrown.
The ``context`` also implements collection methods such as ``__iter__`` and ``__len__``.
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``).
@ -91,3 +135,8 @@ Setup the trigger
First, put the trigger in any path it can be exported, e.g. by packing the resource into python package (which will lead to import path as ``package.slack_reporter.SlackReporter``) or just put file somewhere it can be accessed by application (e.g. ``/usr/local/lib/slack_reporter.py.SlackReporter``).
After that run application as usual and receive notification in your slack channel.
Trigger configuration schema
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Triggers can expose their configuration schema. It can be achieved by implementing ``CONFIGURATION_SCHEMA`` class variable according to `cerberus <https://docs.python-cerberus.org/>`_ documentation. For more details and examples, please refer to built-in triggers implementations.

BIN
github-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,31 +1,36 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=2.3.0rc1
pkgver=2.10.1
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')
url="https://github.com/arcan1s/ahriman"
license=('GPL3')
depends=('devtools' 'git' 'pyalpm' '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'
'python-aiohttp-session: web server with authorization'
'python-boto3: sync to s3'
'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')
@ -49,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

@ -9,4 +9,4 @@ User=ahriman
Group=ahriman
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target

View File

@ -2,11 +2,12 @@
include = ahriman.ini.d
logging = ahriman.ini.d/logging.ini
database = /var/lib/ahriman/ahriman.db
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
@ -23,7 +24,9 @@ build_command = extra-x86_64-build
ignore_packages =
makechrootpkg_flags =
makepkg_flags = --nocolor --ignorearch
triggers = ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger
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]
name = aur-clone
@ -32,6 +35,18 @@ root = /var/lib/ahriman
[sign]
target =
[keyring]
target =
[mirrorlist]
target =
[remote-pull]
target =
[remote-push]
target =
[report]
target = console
@ -64,4 +79,5 @@ debug_check_host = no
debug_allowed_hosts =
host = 127.0.0.1
static_path = /usr/share/ahriman/templates/static
templates = /usr/share/ahriman/templates
templates = /usr/share/ahriman/templates
unix_socket_unsafe = yes

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
@ -20,21 +26,17 @@ formatter = syslog_format
args = ("/dev/log",)
[formatter_generic_format]
format = [%(levelname)s %(asctime)s] [%(threadName)s] [%(name)s]: %(message)s
datefmt =
format = [%(levelname)s %(asctime)s] [%(name)s]: %(message)s
[formatter_syslog_format]
format = [%(levelname)s] [%(threadName)s] [%(name)s]: %(message)s
datefmt =
format = [%(levelname)s] [%(name)s]: %(message)s
[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

@ -12,36 +12,59 @@
<body>
<div class="container">
<h1>ahriman
<img id="badge-version" src="https://img.shields.io/badge/version-unknown-informational" alt="unknown">
<img id="badge-repository" src="https://img.shields.io/badge/repository-unknown-informational" alt="unknown">
<img id="badge-architecture" src="https://img.shields.io/badge/architecture-unknown-informational" alt="unknown">
<img id="badge-status" src="https://img.shields.io/badge/service%20status-unknown-inactive" alt="unknown">
</h1>
</div>
{% include "utils/bootstrap-scripts.jinja2" %}
<div class="container">
<div id="toolbar">
<h1 id="badge-repository">ahriman</h1>
</div>
<div id="alert-placeholder" class="toast-container p3 top-0 start-50 translate-middle-x"></div>
<div class="container">
<div id="toolbar" class="dropdown">
<a id="badge-status" tabindex="0" role="button" class="btn btn-outline-secondary" data-bs-toggle="popover" data-bs-trigger="focus" data-bs-content="no run data"><i class="bi bi-info-circle"></i></a>
{% if not auth.enabled or auth.username is not none %}
<button id="add-btn" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-form" hidden>
<i class="bi bi-plus"></i> add
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-box"></i> packages
</button>
<button id="update-btn" class="btn btn-secondary" onclick="updatePackages()" hidden>
<i class="bi bi-play"></i> update
</button>
<button id="remove-btn" class="btn btn-danger" onclick="removePackages()" disabled hidden>
<i class="bi bi-trash"></i> remove
<ul class="dropdown-menu">
<li>
<button id="package-add-btn" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-add-modal" hidden>
<i class="bi bi-plus"></i> add
</button>
</li>
<li>
<button id="package-update-btn" class="btn dropdown-item" onclick="updatePackages()" hidden>
<i class="bi bi-play"></i> update
</button>
</li>
<li>
<button id="package-rebuild-btn" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-rebuild-modal" hidden>
<i class="bi bi-arrow-clockwise"></i> rebuild
</button>
</li>
<li>
<button id="package-remove-btn" class="btn dropdown-item" onclick="removePackages()" disabled hidden>
<i class="bi bi-trash"></i> remove
</button>
</li>
</ul>
<button id="key-import-btn" type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#key-import-modal" hidden>
<i class="bi bi-key"></i> import key
</button>
{% endif %}
<button class="btn btn-secondary" onclick="reload()">
<button type="button" class="btn btn-secondary" onclick="reload()">
<i class="bi bi-arrow-clockwise"></i> reload
</button>
</div>
<table id="packages" class="table table-striped table-hover"
data-click-to-select="true"
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"
@ -51,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"
@ -61,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>
@ -76,25 +101,30 @@
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
<ul class="nav">
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
<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 %}
<ul class="nav">
<li><a class="nav-link" href="{{ index_url }}" title="repo index">repo index</a></li>
<li><a class="nav-link" href="{{ index_url }}" title="repo index"><i class="bi bi-house"></i> repo index</a></li>
</ul>
{% endif %}
{% if auth.enabled %}
{% if auth.username is none %}
{{ auth.control|safe }}
{% else %}
<form action="/api/v1/logout" method="post">
<button class="btn btn-link" style="text-decoration: none">logout ({{ auth.username }})</button>
</form>
{% endif %}
<ul class="nav">
{% if auth.username is none %}
<li>{{ auth.control|safe }}</li>
{% else %}
<li>
<form action="/api/v1/logout" method="post">
<button class="btn btn-link" style="text-decoration: none"><i class="bi bi-box-arrow-right"></i> logout ({{ auth.username }})</button>
</form>
</li>
{% endif %}
</ul>
{% endif %}
</footer>
</div>
@ -103,12 +133,13 @@
{% include "build-status/login-modal.jinja2" %}
{% endif %}
{% include "utils/bootstrap-scripts.jinja2" %}
{% include "build-status/failed-modal.jinja2" %}
{% include "build-status/success-modal.jinja2" %}
{% include "build-status/alerts.jinja2" %}
{% include "build-status/package-add-modal.jinja2" %}
{% include "build-status/package-rebuild-modal.jinja2" %}
{% include "build-status/key-import-modal.jinja2" %}
{% include "build-status/package-info-modal.jinja2" %}
{% include "build-status/table.jinja2" %}

View File

@ -0,0 +1,45 @@
<script>
const alertPlaceholder = $("#alert-placeholder");
function createAlert(title, message, clz) {
const wrapper = document.createElement("div");
wrapper.classList.add("toast", clz);
wrapper.role = "alert";
wrapper.ariaLive = "assertive";
wrapper.ariaAtomic = "true";
wrapper.style.width = "500px"; // 500px is default modal size
const header = document.createElement("div");
header.classList.add("toast-header");
header.innerHTML = `<strong class="me-auto">${safe(title)}</strong> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="close"></button>`;
wrapper.appendChild(header);
const body = document.createElement("div");
body.classList.add("toast-body", "text-bg-light");
body.innerText = message;
wrapper.appendChild(body);
alertPlaceholder.append(wrapper);
const toast = new bootstrap.Toast(wrapper);
wrapper.addEventListener("hidden.bs.toast", () => {
wrapper.remove(); // bootstrap doesn't remove elements
reload();
});
toast.show();
}
function showFailure(title, description, jqXHR, errorThrown) {
let details;
try {
details = $.parseJSON(jqXHR.responseText).error; // execution handler json error response
} catch (_) {
details = errorThrown;
}
createAlert(title, description(details), "text-bg-danger");
}
function showSuccess(title, description) {
createAlert(title, description, "text-bg-success");
}
</script>

View File

@ -1,28 +0,0 @@
<div id="failed-form" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-danger">
<h4 class="modal-title">failed</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<p>Packages update has failed.</p>
<p id="error-details"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
</div>
</div>
</div>
</div>
<script>
const failedForm = $("#failed-form");
const errorDetails = $("#error-details");
failedForm.on("hidden.bs.modal", () => { reload(); });
function showFailure(details) {
errorDetails.text(details);
failedForm.modal("show");
}
</script>

View File

@ -0,0 +1,93 @@
<div id="key-import-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<form id="key-import-form" onsubmit="return false">
<div class="modal-header">
<h4 class="modal-title">Import key from PGP server</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="key-fingerprint-input" class="col-sm-2 col-form-label">fingerprint</label>
<div class="col-sm-10">
<input id="key-fingerprint-input" type="text" class="form-control" placeholder="PGP key fingerprint" name="key" required>
</div>
</div>
<div class="form-group row">
<label for="key-server-input" class="col-sm-2 col-form-label">key server</label>
<div class="col-sm-10">
<input id="key-server-input" type="text" class="form-control" placeholder="PGP key server" name="server" value="keyserver.ubuntu.com" required>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<pre class="language-less"><samp id="key-body-input" class="pre-scrollable language-less"></samp><button id="key-copy-btn" type="button" class="btn language-less" onclick="copyPgpKey()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" onclick="importPgpKey()"><i class="bi bi-play"></i> import</button>
<button type="submit" class="btn btn-success" onclick="fetchPgpKey()"><i class="bi bi-arrow-clockwise"></i> fetch</button>
</div>
</form>
</div>
</div>
</div>
<script>
const keyImportModal = $("#key-import-modal");
const keyImportForm = $("#key-import-form");
keyImportModal.on("hidden.bs.modal", () => {
keyBodyInput.text("");
keyImportForm.trigger("reset");
});
const keyBodyInput = $("#key-body-input");
const keyCopyButton = $("#key-copy-btn");
const keyFingerprintInput = $("#key-fingerprint-input");
const keyServerInput = $("#key-server-input");
async function copyPgpKey() {
const logs = keyBodyInput.text();
await copyToClipboard(logs, keyCopyButton);
}
function fetchPgpKey() {
const key = keyFingerprintInput.val();
const server = keyServerInput.val();
if (key && server) {
$.ajax({
url: "/api/v1/service/pgp",
data: {"key": key, "server": server},
type: "GET",
dataType: "json",
success: response => { keyBodyInput.text(response.key); },
});
}
}
function importPgpKey() {
const key = keyFingerprintInput.val();
const server = keyServerInput.val();
if (key && server) {
$.ajax({
url: "/api/v1/service/pgp",
data: JSON.stringify({key: key, server: server}),
type: "POST",
contentType: "application/json",
success: _ => {
keyImportModal.modal("hide");
showSuccess("Success", `Key ${key} has been imported`);
},
error: (jqXHR, _, errorThrown) => {
const message = _ => `Could not import key ${key} from ${server}`;
showFailure("Action failed", message, jqXHR, errorThrown);
},
});
}
}
</script>

View File

@ -1,9 +1,9 @@
<div id="loginForm" tabindex="-1" role="dialog" class="modal fade">
<div id="login-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="/api/v1/login" method="post">
<div class="modal-header">
<h4 class="modal-title">login</h4>
<h4 class="modal-title">Login</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
@ -16,14 +16,36 @@
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">password</label>
<div class="col-sm-10">
<input id="password" type="password" class="form-control" placeholder="enter password" name="password" required>
<div class="input-group">
<input id="password" type="password" class="form-control" placeholder="enter password" name="password" required>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" onclick="showPassword()"><i id="show-hide-password-btn" class="bi bi-eye"></i></button>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary">login</button>
<button class="btn btn-primary"><i class="bi bi-person"></i> login</button>
</div>
</form>
</div>
</div>
</div>
<script>
const passwordInput = $("#password");
const showHidePasswordButton = $("#show-hide-password-btn");
function showPassword() {
if (passwordInput.attr("type") === "password") {
passwordInput.attr("type", "text");
showHidePasswordButton.removeClass("bi-eye");
showHidePasswordButton.addClass("bi-eye-slash");
} else {
passwordInput.attr("type", "password");
showHidePasswordButton.removeClass("bi-eye-slash");
showHidePasswordButton.addClass("bi-eye");
}
}
</script>

View File

@ -1,61 +1,78 @@
<div id="add-form" tabindex="-1" role="dialog" class="modal fade">
<div id="package-add-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">add new packages</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="package" class="col-sm-2 col-form-label">package</label>
<div class="col-sm-10">
<input id="package-form" type="text" list="known-packages-dlist" autocomplete="off" class="form-control" placeholder="AUR package" name="package" required>
<datalist id="known-packages-dlist"></datalist>
<form id="package-add-form" onsubmit="return false">
<div class="modal-header">
<h4 class="modal-title">Add new packages</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="package-input" class="col-sm-2 col-form-label">package</label>
<div class="col-sm-10">
<input id="package-input" type="text" list="known-packages-dlist" autocomplete="off" class="form-control" placeholder="AUR package" name="package" required>
<datalist id="known-packages-dlist"></datalist>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">close</button>
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="requestPackages()">request</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPackages()">add</button>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" onclick="packagesAdd()"><i class="bi bi-play"></i> add</button>
<button type="submit" class="btn btn-success" onclick="packagesRequest()"><i class="bi bi-plus"></i> request</button>
</div>
</form>
</div>
</div>
</div>
<script>
const packageInput = $("#package-form");
const knownPackages = $("#known-packages-dlist");
const packageAddModal = $("#package-add-modal");
const packageAddForm = $("#package-add-form");
packageAddModal.on("hidden.bs.modal", () => { packageAddForm.trigger("reset"); });
const packageInput = $("#package-input");
const knownPackagesList = $("#known-packages-dlist");
packageInput.keyup(() => {
clearTimeout(packageInput.data("timeout"));
packageInput.data("timeout", setTimeout($.proxy(() => {
const value = packageInput.val();
$.ajax({
url: "/api/v1/service/search",
data: {"for": value},
type: "GET",
dataType: "json",
success: response => {
const options = response.map(pkg => {
const option = document.createElement("option");
option.value = pkg.package;
option.innerText = `${pkg.package} (${pkg.description})`;
return option;
});
knownPackages.empty().append(options);
},
})
if (value.length >= 3) {
$.ajax({
url: "/api/v1/service/search",
data: {"for": value},
type: "GET",
dataType: "json",
success: response => {
const options = response.map(pkg => {
const option = document.createElement("option");
option.value = pkg.package;
option.innerText = `${pkg.package} (${pkg.description})`;
return option;
});
knownPackagesList.empty().append(options);
},
});
}
}, this), 500));
});
function addPackages() {
const packages = [packageInput.val()];
doPackageAction("/api/v1/service/add", packages);
function packagesAdd() {
const packages = packageInput.val();
if (packages) {
packageAddModal.modal("hide");
const onSuccess = update => `Packages ${update} have been added`;
const onFailure = error => `Package addition failed: ${error}`;
doPackageAction("/api/v1/service/add", [packages], onSuccess, onFailure);
}
}
function requestPackages() {
const packages = [packageInput.val()];
doPackageAction("/api/v1/service/request", packages);
function packagesRequest() {
const packages = packageInput.val();
if (packages) {
packageAddModal.modal("hide");
const onSuccess = update => `Packages ${update} have been requested`;
const onFailure = error => `Package request failed: ${error}`;
doPackageAction("/api/v1/service/request", [packages], onSuccess, onFailure);
}
}
</script>

View File

@ -0,0 +1,70 @@
<div id="package-info-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div id="package-info-modal-header" class="modal-header">
<h4 id="package-info" class="modal-title"></h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<pre class="language-logs"><samp id="package-info-logs-input" class="pre-scrollable language-logs"></samp><button id="logs-copy-btn" type="button" class="btn language-logs" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="showLogs()"><i class="bi bi-arrow-clockwise"></i> reload</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
</div>
</div>
</div>
</div>
<script>
const packageInfoModal = $("#package-info-modal");
const packageInfoModalHeader = $("#package-info-modal-header");
const packageInfo = $("#package-info");
const packageInfoLogsInput = $("#package-info-logs-input");
const packageInfoLogsCopyButton = $("#logs-copy-btn");
async function copyLogs() {
const logs = packageInfoLogsInput.text();
await copyToClipboard(logs, packageInfoLogsCopyButton);
}
function showLogs(packageBase) {
const isPackageBaseSet = packageBase !== undefined;
if (isPackageBaseSet)
packageInfoModal.data("package", packageBase); // set package base as currently used
else
packageBase = packageInfoModal.data("package"); // read package base from the current window attribute
const headerClass = status => {
if (status === "pending") return ["bg-warning"];
if (status === "building") return ["bg-warning"];
if (status === "failed") return ["bg-danger", "text-white"];
if (status === "success") return ["bg-success", "text-white"];
return ["bg-secondary", "text-white"];
};
$.ajax({
url: `/api/v1/packages/${packageBase}/logs`,
type: "GET",
dataType: "json",
success: response => {
packageInfo.text(`${response.package_base} ${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`);
packageInfoLogsInput.text(response.logs);
packageInfoModalHeader.removeClass();
packageInfoModalHeader.addClass("modal-header");
headerClass(response.status.status).forEach((clz) => packageInfoModalHeader.addClass(clz));
if (isPackageBaseSet) packageInfoModal.modal("show"); // we don't need to show window again
},
error: (jqXHR, _, errorThrown) => {
// show failed modal in case if first time loading
if (isPackageBaseSet) {
const message = error => `Could not load package ${packageBase} logs: ${error}`;
showFailure("Load failure", message, jqXHR, errorThrown);
}
},
});
}
</script>

View File

@ -0,0 +1,41 @@
<div id="package-rebuild-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form id="package-rebuild-form" onsubmit="return false">
<div class="modal-header">
<h4 class="modal-title">Rebuild depending packages</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="dependency-input" class="col-sm-4 col-form-label">dependency</label>
<div class="col-sm-8">
<input id="dependency-input" type="text" class="form-control" placeholder="packages dependency" name="package" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" onclick="packagesRebuild()"><i class="bi bi-play"></i> rebuild</button>
</div>
</form>
</div>
</div>
</div>
<script>
const packageRebuildModal = $("#package-rebuild-modal");
const packageRebuildForm = $("#package-rebuild-form");
packageRebuildModal.on("hidden.bs.modal", () => { packageRebuildForm.trigger("reset"); });
const dependencyInput = $("#dependency-input");
function packagesRebuild() {
const packages = dependencyInput.val();
if (packages) {
packageRebuildModal.modal("hide");
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);
}
}
</script>

View File

@ -1,28 +0,0 @@
<div id="success-form" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-success">
<h4 class="modal-title">success</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<p>Packages update has been run.</p>
<ul id="success-details"></ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
</div>
</div>
</div>
</div>
<script>
const successForm = $("#success-form");
const successDetails = $("#success-details");
successForm.on("hidden.bs.modal", () => { reload(); });
function showSuccess(details) {
successDetails.empty().append(details);
successForm.modal("show");
}
</script>

View File

@ -1,54 +1,98 @@
<script>
const addButton = $("#add-btn");
const removeButton = $("#remove-btn");
const updateButton = $("#update-btn");
const keyImportButton = $("#key-import-btn");
const packageAddButton = $("#package-add-btn");
const packageRebuildButton = $("#package-rebuild-btn");
const packageRemoveButton = $("#package-remove-btn");
const packageUpdateButton = $("#package-update-btn");
const table = $("#packages");
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table",
() => {
removeButton.prop("disabled", !table.bootstrapTable("getSelections").length);
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table", () => {
packageRemoveButton.prop("disabled", !table.bootstrapTable("getSelections").length);
});
table.on("click-row.bs.table", (self, data, row, cell) => {
if (0 === cell || "base" === cell) {
const method = data[0] === true ? "uncheckBy" : "checkBy"; // fck javascript
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",
},
});
const architectureBadge = $("#badge-architecture");
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");
const versionBadge = $("#badge-version");
function doPackageAction(uri, packages) {
function doPackageAction(uri, packages, successText, failureText) {
$.ajax({
url: uri,
data: JSON.stringify({packages: packages}),
type: "POST",
contentType: "application/json",
success: _ => {
const details = packages.map(pkg => {
const li = document.createElement("li");
li.innerText = pkg;
return li;
});
showSuccess(details);
const message = successText(packages.join(", "));
showSuccess("Success", message);
},
error: (jqXHR, _, errorThrown) => {
showFailure("Action failed", failureText, jqXHR, errorThrown);
},
error: (jqXHR, _, errorThrown) => { showFailure(errorThrown); },
});
}
function getSelection() {
return table.bootstrapTable("getSelections").map(row => { return row.id; });
return table.bootstrapTable("getSelections").map(row => row.id);
}
function removePackages() { doPackageAction("/api/v1/service/remove", getSelection()); }
function removePackages() {
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() { doPackageAction("/api/v1/service/add", getSelection()); }
function updatePackages() {
const currentSelection = getSelection();
const [url, onSuccess] = currentSelection.length === 0
? ["/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);
}
function hideControls(hidden) {
addButton.attr("hidden", hidden);
removeButton.attr("hidden", hidden);
updateButton.attr("hidden", hidden);
keyImportButton.attr("hidden", hidden);
packageAddButton.attr("hidden", hidden);
packageRebuildButton.attr("hidden", hidden);
packageRemoveButton.attr("hidden", hidden);
packageUpdateButton.attr("hidden", hidden);
}
function reload() {
table.bootstrapTable("showLoading");
const badgeClass = status => {
if (status === "pending") return "btn-outline-warning";
if (status === "building") return "btn-outline-warning";
if (status === "failed") return "btn-outline-danger";
if (status === "success") return "btn-outline-success";
return "btn-outline-secondary";
};
$.ajax({
url: "/api/v1/packages",
type: "GET",
@ -56,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>");
};
@ -73,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")),
@ -95,7 +140,8 @@
table.bootstrapTable("hideLoading");
} else {
// other errors
showFailure(errorThrown);
const message = error => `Could not load list of packages: ${error}`;
showFailure("Load failure", message, jqXHR, errorThrown);
}
hideControls(true);
},
@ -106,39 +152,20 @@
type: "GET",
dataType: "json",
success: response => {
const badgeColor = status => {
if (status === "pending") return "yellow";
if (status === "building") return "yellow";
if (status === "failed") return "critical";
if (status === "success") return "success";
return "inactive";
};
repositoryBadge.text(`${response.repository} ${response.architecture}`);
versionBadge.html(`<i class="bi bi-github"></i> ahriman ${safe(response.version)}`);
architectureBadge
.attr("src", `https://img.shields.io/badge/architecture-${response.architecture}-informational`)
.attr("alt", response.architecture);
repositoryBadge
.attr("src", `https://img.shields.io/badge/repository-${response.repository.replace(/-/g, "--")}-informational`)
.attr("alt", response.repository);
statusBadge
.attr("src", `https://img.shields.io/badge/service%20status-${response.status.status}-${badgeColor(response.status.status)}`)
.attr("alt", response.status.status)
.attr("title", `at ${new Date(1000 * response.status.timestamp).toISOString()}`);
versionBadge
.attr("src", `https://img.shields.io/badge/version-${response.version}-informational`)
.attr("alt", response.version);
.popover("dispose")
.attr("data-bs-content", `${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`)
.popover();
statusBadge.removeClass();
statusBadge.addClass("btn");
statusBadge.addClass(badgeClass(response.status.status));
},
});
}
function safe(string) {
return String(string)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function statusFormat(value) {
const cellClass = status => {
if (status === "pending") return "table-warning";
@ -150,8 +177,21 @@
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();
reload();
});
</script>

View File

@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<title>Error</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/static/favicon.ico">
{% include "utils/style.jinja2" %}
</head>
<body>
{% include "utils/bootstrap-scripts.jinja2" %}
<div class="d-flex flex-row align-items-center">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12 text-center">
<span class="display-1 d-block">{{ code }}</span>
<div class="mb-4 lead">{{ reason }}</div>
<a class="btn btn-link" style="text-decoration: none" href="/" title="home"><i class="bi bi-house"></i> home</a>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -10,24 +10,29 @@
<body>
{% include "utils/bootstrap-scripts.jinja2" %}
<div class="container">
<h1>Arch linux user repository</h1>
</div>
<div class="container">
{% if pgp_key is not none %}
<p>This repository is signed with <a href="https://pgp.mit.edu/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
<p>This repository is signed with <a href="https://keyserver.ubuntu.com/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
{% endif %}
<pre>$ cat /etc/pacman.conf
[{{ repository }}]
<p>In order to use this repository edit your <code>/etc/pacman.conf</code> as following:</p>
<pre class="language-ini"><code id="pacman-conf" class="language-ini">[{{ repository }}]
Server = {{ link_path }}
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly</pre>
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly</code><button id="copy-btn" type="button" class="btn language-ini" onclick="copyPacmanConf()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
<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"
@ -37,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"
@ -45,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>
@ -83,16 +89,57 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
<ul class="nav">
{% if homepage is not none %}
<li><a class="nav-link" href="{{ homepage }}" title="homepage">homepage</a></li>
<li><a class="nav-link" href="{{ homepage }}" title="homepage"><i class="bi bi-house"></i> homepage</a></li>
{% endif %}
</ul>
<ul class="nav">
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
</ul>
</footer>
</div>
{% include "utils/bootstrap-scripts.jinja2" %}
<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");
async function copyPacmanConf() {
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,12 +1,70 @@
<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/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-table@1.20.2/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.20.2/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.20.2/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) {
if (navigator.clipboard === undefined) {
const input = document.createElement("textarea");
input.innerHTML = text;
document.body.appendChild(input);
input.select();
document.execCommand("copy");
document.body.removeChild(input);
} else {
await navigator.clipboard.writeText(text);
}
button.html("<i class=\"bi bi-clipboard-check\"></i> copied");
setTimeout(()=> {
button.html("<i class=\"bi bi-clipboard\"></i> copy");
}, 2000);
}
function safe(string) {
return String(string)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.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,9 +1,30 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/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.20.2/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 href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
<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/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 {
display: block;
max-height: 680px;
overflow-y: scroll;
}
pre[class*="language-"] {
position: relative;
}
pre[class*="language-"] button {
position: absolute;
top: 0;
right: 5px;
}
</style>

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,14 +25,15 @@ setup(
packages=find_packages("src"),
package_dir={"": "src"},
package_data={"": ["py.typed"]},
dependency_links=[
],
install_requires=[
"cerberus",
"inflection",
"passlib",
"requests",
"setuptools",
"srcinfo",
],
setup_requires=[
@ -52,29 +53,36 @@ setup(
"package/bin/ahriman",
],
data_files=[
# configuration
("share/ahriman/settings", [
"package/share/ahriman/settings/ahriman.ini",
]),
("share/ahriman/settings/ahriman.ini.d", [
"package/share/ahriman/settings/ahriman.ini.d/logging.ini",
]),
# systemd files
("lib/systemd/system", [
"package/lib/systemd/system/ahriman@.service",
"package/lib/systemd/system/ahriman@.timer",
"package/lib/systemd/system/ahriman-web@.service",
]),
# 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",
"package/share/ahriman/templates/repo-index.jinja2",
"package/share/ahriman/templates/shell",
"package/share/ahriman/templates/telegram-index.jinja2",
]),
("share/ahriman/templates/build-status", [
"package/share/ahriman/templates/build-status/failed-modal.jinja2",
"package/share/ahriman/templates/build-status/alerts.jinja2",
"package/share/ahriman/templates/build-status/key-import-modal.jinja2",
"package/share/ahriman/templates/build-status/login-modal.jinja2",
"package/share/ahriman/templates/build-status/package-add-modal.jinja2",
"package/share/ahriman/templates/build-status/success-modal.jinja2",
"package/share/ahriman/templates/build-status/package-info-modal.jinja2",
"package/share/ahriman/templates/build-status/package-rebuild-modal.jinja2",
"package/share/ahriman/templates/build-status/table.jinja2",
]),
("share/ahriman/templates/static", [
@ -84,9 +92,17 @@ setup(
"package/share/ahriman/templates/utils/bootstrap-scripts.jinja2",
"package/share/ahriman/templates/utils/style.jinja2",
]),
# man pages
("share/man/man1", [
"docs/ahriman.1",
])
]),
# shell completions
("share/bash-completion/completions", [
"docs/completions/bash/_ahriman",
]),
("share/zsh/site-functions", [
"docs/completions/zsh/_ahriman",
]),
],
extras_require={
@ -100,9 +116,14 @@ setup(
"Sphinx",
"argparse-manpage",
"pydeps",
"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
# really need to run the application we can move it to "optional" dependencies
@ -123,13 +144,16 @@ setup(
],
"web": [
"Jinja2",
"aiohttp",
"aiohttp_jinja2",
"aioauth-client",
"aiohttp",
"aiohttp-apispec",
"aiohttp_cors",
"aiohttp_jinja2",
"aiohttp_debugtoolbar",
"aiohttp_session",
"aiohttp_security",
"cryptography",
"requests-unixsocket", # required by unix socket support
],
},
)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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
@ -68,12 +70,16 @@ def _parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="ahriman", description="ArcH linux ReposItory MANager",
epilog="Argument list can also be read from file by using @ prefix.",
fromfile_prefix_chars="@", formatter_class=_formatter)
parser.add_argument("-a", "--architecture", help="target architectures (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("-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("--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")
@ -84,10 +90,10 @@ def _parser() -> argparse.ArgumentParser:
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command", required=True)
_set_aur_search_parser(subparsers)
_set_daemon_parser(subparsers)
_set_help_parser(subparsers)
_set_help_commands_unsafe_parser(subparsers)
_set_key_import_parser(subparsers)
_set_help_updates_parser(subparsers)
_set_help_version_parser(subparsers)
_set_package_add_parser(subparsers)
_set_package_remove_parser(subparsers)
_set_package_status_parser(subparsers)
@ -99,23 +105,28 @@ def _parser() -> argparse.ArgumentParser:
_set_patch_set_add_parser(subparsers)
_set_repo_backup_parser(subparsers)
_set_repo_check_parser(subparsers)
_set_repo_clean_parser(subparsers)
_set_repo_config_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)
_set_repo_report_parser(subparsers)
_set_repo_restore_parser(subparsers)
_set_repo_setup_parser(subparsers)
_set_repo_sign_parser(subparsers)
_set_repo_status_update_parser(subparsers)
_set_repo_sync_parser(subparsers)
_set_repo_tree_parser(subparsers)
_set_repo_triggers_parser(subparsers)
_set_repo_update_parser(subparsers)
_set_shell_parser(subparsers)
_set_service_clean_parser(subparsers)
_set_service_config_parser(subparsers)
_set_service_config_validate_parser(subparsers)
_set_service_key_import_parser(subparsers)
_set_service_setup_parser(subparsers)
_set_service_shell_parser(subparsers)
_set_user_add_parser(subparsers)
_set_user_list_parser(subparsers)
_set_user_remove_parser(subparsers)
_set_version_parser(subparsers)
_set_web_parser(subparsers)
return parser
@ -145,35 +156,6 @@ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
def _set_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for daemon subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("daemon", help="run application as daemon",
description="start process which periodically will run update process",
formatter_class=_formatter)
parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
parser.add_argument("--aur", help="enable or disable checking for AUR updates. Implies --no-vcs",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--manual", help="include or exclude manual updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
action=argparse.BooleanOptionalAction, default=True)
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=0)
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
return parser
def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for listing help subcommand
@ -188,8 +170,8 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="show help message for application or command and exit",
formatter_class=_formatter)
parser.add_argument("command", help="show help message for specific command", nargs="?")
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True, parser=_parser)
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, quiet=True, unsafe=True,
parser=_parser)
return parser
@ -205,16 +187,16 @@ 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
def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
def _set_help_updates_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for key import subcommand
add parser for service update check subcommand
Args:
root(SubParserAction): subparsers for the commands
@ -222,16 +204,28 @@ def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("key-import", help="import PGP key",
description="import PGP key from public sources to the repository user",
epilog="By default ahriman runs build process with package sources validation "
"(in case if signature and keys are available in PKGBUILD). This process will "
"fail in case if key is not known for build user. This subcommand can be used "
"in order to import the PGP key to user keychain.",
parser = root.add_parser("help-updates", help="check for service updates",
description="request AUR for current version and compare with current service version",
formatter_class=_formatter)
parser.add_argument("--key-server", help="key server for key import", default="pgp.mit.edu")
parser.add_argument("key", help="PGP key to import from public server")
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, report=False)
parser.add_argument("-e", "--exit-code", help="return non-zero exit code if updates available", action="store_true")
parser.set_defaults(handler=handlers.ServiceUpdates, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True)
return parser
def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for version subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("help-version", aliases=["version"], help="application version",
description="print application and its dependencies versions", formatter_class=_formatter)
parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, report=False, quiet=True, unsafe=True)
return parser
@ -259,14 +253,16 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"5) and finally you can add package from AUR.",
formatter_class=_formatter)
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
parser.add_argument("--dependencies", help="process missing package dependencies",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
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=0)
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("--without-dependencies", help="do not add dependencies", action="store_true")
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
parser.set_defaults(handler=handlers.Add)
return parser
@ -305,7 +301,8 @@ def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser
parser.add_argument("package", help="filter status by package base", nargs="*")
parser.add_argument("--ahriman", help="get service status itself", 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("-i", "--info", help="show additional package information", action="store_true")
parser.add_argument("--info", help="show additional package information",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("-s", "--status", help="filter packages by status",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
parser.set_defaults(handler=handlers.Status, lock=None, report=False, quiet=True, unsafe=True)
@ -397,7 +394,8 @@ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
action="append")
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, report=False)
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, report=False,
unsafe=True)
return parser
@ -479,18 +477,19 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
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, "
"-yy to force refresh even if up to date",
action="count", default=0)
parser.set_defaults(handler=handlers.Update, dry_run=True, aur=True, local=True, manual=False)
action="count", default=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_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
def _set_repo_create_keyring_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository clean subcommand
add parser for create-keyring subcommand
Args:
root(SubParserAction): subparsers for the commands
@ -498,28 +497,18 @@ def _set_repo_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-clean", aliases=["clean"], help="clean local caches",
description="remove local caches",
epilog="The subcommand clears every temporary directories (builds, caches etc). Normally "
"you should not run this command manually. Also in case if you are going to clear "
"the chroot directories you will need root privileges.",
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.add_argument("--cache", help="clear directory with package caches",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--manual", help="clear manually added packages queue",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--packages", help="clear directory with built packages",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--pacman", help="clear directory with pacman local database cache",
action=argparse.BooleanOptionalAction, default=False)
parser.set_defaults(handler=handlers.Clean, quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.support.KeyringTrigger"])
return parser
def _set_repo_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
def _set_repo_create_mirrorlist_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for config subcommand
add parser for create-mirrorlist subcommand
Args:
root(SubParserAction): subparsers for the commands
@ -527,10 +516,43 @@ def _set_repo_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-config", aliases=["config"], help="dump configuration",
description="dump configuration for the specified architecture",
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.Dump, lock=None, report=False, quiet=True, unsafe=True)
parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.support.MirrorlistTrigger"])
return parser
def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for daemon subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-daemon", aliases=["daemon"], help="run application as daemon",
description="start process which periodically will run update process",
formatter_class=_formatter)
parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
parser.add_argument("--aur", help="enable or disable checking for AUR updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dependencies", help="process missing package dependencies",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--manual", help="include or exclude manual updates",
action=argparse.BooleanOptionalAction, default=True)
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, "
"-yy to force refresh even if up to date",
action="count", default=False)
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
return parser
@ -556,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
@ -614,36 +639,6 @@ def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for setup subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-setup", aliases=["init", "repo-init", "setup"], help="initial service configuration",
description="create initial service configuration, requires root",
epilog="Create _minimal_ configuration for the service according to provided options.",
formatter_class=_formatter)
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"))
parser.add_argument("--multilib", help="add or do not multilib repository",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--packager", help="packager name and email", required=True)
parser.add_argument("--repository", help="repository name", required=True)
parser.add_argument("--sign-key", help="sign key id")
parser.add_argument("--sign-target", help="sign options", action="append",
type=SignSettings.from_option, choices=enum_values(SignSettings))
parser.add_argument("--web-port", help="port of the web service", type=int)
parser.set_defaults(handler=handlers.Setup, lock=None, report=False, quiet=True, unsafe=True)
return parser
def _set_repo_sign_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for sign subcommand
@ -700,6 +695,23 @@ def _set_repo_sync_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
def _set_repo_tree_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository tree subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-tree", help="dump repository tree",
description="dump repository tree based on packages dependencies",
formatter_class=_formatter)
parser.set_defaults(handler=handlers.Structure, lock=None, report=False, quiet=True, unsafe=True)
return parser
def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository triggers subcommand
@ -714,7 +726,7 @@ def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="run triggers on empty build result as configured by settings",
formatter_class=_formatter)
parser.add_argument("trigger", help="instead of running all triggers as set by configuration, just process "
"specified ones oin order of metion", nargs="*")
"specified ones in order of mention", nargs="*")
parser.set_defaults(handler=handlers.Triggers)
return parser
@ -733,24 +745,155 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="check for packages updates and run build process if requested",
formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("--aur", help="enable or disable checking for AUR updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dependencies", help="process missing package dependencies",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", 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("--aur", help="enable or disable checking for AUR updates. Implies --no-vcs",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--manual", help="include or exclude manual updates",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
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, "
"-yy to force refresh even if up to date",
action="count", default=0)
action="count", default=False)
parser.set_defaults(handler=handlers.Update)
return parser
def _set_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
def _set_service_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository clean subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("service-clean", aliases=["clean", "repo-clean"], help="clean local caches",
description="remove local caches",
epilog="The subcommand clears every temporary directories (builds, caches etc). Normally "
"you should not run this command manually. Also in case if you are going to clear "
"the chroot directories you will need root privileges.",
formatter_class=_formatter)
parser.add_argument("--cache", help="clear directory with package caches",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--manual", help="clear manually added packages queue",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--packages", help="clear directory with built packages",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--pacman", help="clear directory with pacman local database cache",
action=argparse.BooleanOptionalAction, default=False)
parser.set_defaults(handler=handlers.Clean, quiet=True, unsafe=True)
return parser
def _set_service_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for config subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("service-config", aliases=["config", "repo-config"], help="dump configuration",
description="dump configuration for the specified architecture",
formatter_class=_formatter)
parser.add_argument("--secure", help="hide passwords and secrets from output",
action=argparse.BooleanOptionalAction, default=True)
parser.set_defaults(handler=handlers.Dump, lock=None, report=False, quiet=True, unsafe=True)
return parser
def _set_service_config_validate_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for config validation subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("service-config-validate", aliases=["config-validate", "repo-config-validate"],
help="validate system configuration",
description="validate configuration and print found errors",
formatter_class=_formatter)
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if configuration is invalid",
action="store_true")
parser.set_defaults(handler=handlers.Validate, lock=None, report=False, quiet=True, unsafe=True)
return parser
def _set_service_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for key import subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("service-key-import", aliases=["key-import"], help="import PGP key",
description="import PGP key from public sources to the repository user",
epilog="By default ahriman runs build process with package sources validation "
"(in case if signature and keys are available in PKGBUILD). This process will "
"fail in case if key is not known for build user. This subcommand can be used "
"in order to import the PGP key to user keychain.",
formatter_class=_formatter)
parser.add_argument("--key-server", help="key server for key import", default="keyserver.ubuntu.com")
parser.add_argument("key", help="PGP key to import from public server")
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, report=False)
return parser
def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for setup subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("service-setup", aliases=["init", "repo-init", "repo-setup", "setup"],
help="initial service configuration",
description="create initial service configuration, requires root",
epilog="Create _minimal_ configuration for the service according to provided options.",
formatter_class=_formatter)
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.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")
parser.add_argument("--multilib", help="add or do not multilib repository",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--packager", help="packager name and email", required=True)
parser.add_argument("--repository", help="repository name", required=True)
parser.add_argument("--sign-key", help="sign key id")
parser.add_argument("--sign-target", help="sign options", action="append",
type=SignSettings.from_option, choices=enum_values(SignSettings))
parser.add_argument("--web-port", help="port of the web service", type=int)
parser.add_argument("--web-unix-socket", help="path to unix socket used for interprocess communications", type=Path)
parser.set_defaults(handler=handlers.Setup, lock=None, report=False, quiet=True, unsafe=True)
return parser
def _set_service_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for shell subcommand
@ -760,9 +903,8 @@ def _set_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("shell", help="envoke python shell",
description="drop into python shell while having created application",
formatter_class=_formatter)
parser = root.add_parser("service-shell", aliases=["shell"], help="invoke python shell",
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)
@ -784,14 +926,15 @@ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"In case if password was not entered it will be asked interactively",
formatter_class=_formatter)
parser.add_argument("username", help="username for web service")
parser.add_argument("--as-service", help="add user as service user", action="store_true")
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, unsafe=True)
quiet=True)
return parser
@ -811,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
@ -830,26 +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.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False, # nosec
password="", quiet=True, unsafe=True)
return parser
def _set_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for version subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("version", help="application version",
description="print application and its dependencies versions", formatter_class=_formatter)
parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, report=False, quiet=True,
unsafe=True)
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False,
quiet=True)
return parser
@ -864,7 +989,8 @@ def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("web", help="web server", description="start web server", formatter_class=_formatter)
parser.set_defaults(handler=handlers.Web, lock=None, report=False, parser=_parser)
parser.set_defaults(handler=handlers.Web, lock=Path(tempfile.gettempdir()) / "ahriman-web.lock", report=False,
parser=_parser)
return parser

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -17,10 +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 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
@ -36,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)
@ -49,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():
@ -87,3 +90,62 @@ class Application(ApplicationPackages, ApplicationRepository):
directly as it will be called after on_start action
"""
self.repository.triggers.on_stop()
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
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]) -> 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
}
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
known_packages = self._known_packages()
with_dependencies = {package.base: package for package in packages}
while missing := missing_dependencies(with_dependencies.values()):
for package_name, username in missing.items():
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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -20,12 +20,13 @@
import requests
import shutil
from collections.abc import Iterable
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Any, Iterable, Set
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
@ -37,17 +38,116 @@ class ApplicationPackages(ApplicationProperties):
package control class
"""
def _known_packages(self) -> Set[str]:
def _add_archive(self, source: str, *_: Any) -> None:
"""
load packages from repository and pacman repositories
add package from archive
Returns:
Set[str]: list of known packages
Args:
source(str): path to package archive
Raises:
NotImplementedError: not implemented method
UnknownPackageError: if specified path doesn't exist
"""
raise NotImplementedError
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, 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, username)
self.database.build_queue_insert(package)
self.database.remote_update(package)
def _add_directory(self, source: str, *_: Any) -> None:
"""
add packages from directory
Args:
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, 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
"""
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)
def _add_remote(self, source: str, *_: Any) -> None:
"""
add package from remote sources (e.g. HTTP)
Args:
source(str): remote URL of the package archive
"""
dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
# 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, 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, username)
self.database.build_queue_insert(package)
self.database.remote_update(package)
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, username)
def on_result(self, result: Result) -> None:
"""
@ -61,123 +161,6 @@ class ApplicationPackages(ApplicationProperties):
"""
raise NotImplementedError
def _add_archive(self, source: str, *_: Any) -> None:
"""
add package from archive
Args:
source(str): path to package archive
"""
local_path = Path(source)
dst = self.repository.paths.packages / local_path.name
shutil.copy(local_path, dst)
def _add_aur(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None:
"""
add package from AUR
Args:
source(str): package base name
known_packages(Set[str]): list of packages which are known by the service
without_dependencies(bool): if set, dependency check will be disabled
"""
package = Package.from_aur(source, self.repository.pacman)
self.database.build_queue_insert(package)
self.database.remote_update(package)
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (local_dir := Path(dir_name)):
Sources.load(local_dir, package, self.database.patches_get(package.base), self.repository.paths)
self._process_dependencies(local_dir, known_packages, without_dependencies)
def _add_directory(self, source: str, *_: Any) -> None:
"""
add packages from directory
Args:
source(str): path to local directory
"""
local_dir = Path(source)
for full_path in filter(package_like, local_dir.iterdir()):
self._add_archive(str(full_path))
def _add_local(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None:
"""
add package from local PKGBUILDs
Args:
source(str): path to directory with local source files
known_packages(Set[str]): list of packages which are known by the service
without_dependencies(bool): if set, dependency check will be disabled
"""
source_dir = Path(source)
package = Package.from_build(source_dir)
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
self.database.build_queue_insert(package)
self._process_dependencies(cache_dir, known_packages, without_dependencies)
def _add_remote(self, source: str, *_: Any) -> None:
"""
add package from remote sources (e.g. HTTP)
Args:
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()
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:
"""
add package from official repository
Args:
source(str): package base name
"""
package = Package.from_official(source, self.repository.pacman)
self.database.build_queue_insert(package)
self.database.remote_update(package)
# repository packages must not depend on unknown packages, thus we are not going to process dependencies
def _process_dependencies(self, local_dir: Path, known_packages: Set[str], without_dependencies: bool) -> None:
"""
process package dependencies
Args:
local_dir(Path): path to local package sources (i.e. cloned AUR repository)
known_packages(Set[str]): list of packages which are known by the service
without_dependencies(bool): if set, dependency check will be disabled
"""
if without_dependencies:
return
dependencies = Package.dependencies(local_dir)
self.add(dependencies.difference(known_packages), PackageSource.AUR, without_dependencies)
def add(self, names: Iterable[str], source: PackageSource, without_dependencies: bool) -> None:
"""
add packages for the next build
Args:
names(Iterable[str]): list of package bases to add
source(PackageSource): package source to add
without_dependencies(bool): if set, dependency check will be disabled
"""
known_packages = self._known_packages() # speedup dependencies processing
for name in names:
resolved_source = source.resolve(name)
fn = getattr(self, f"_add_{resolved_source.value}")
fn(name, known_packages, without_dependencies)
def remove(self, names: Iterable[str]) -> None:
"""
remove packages from repository

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -19,8 +19,9 @@
#
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.lazy_logging import LazyLogging
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,11 +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): pacman database syncronization level, ``0`` is disabled
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(architecture, configuration, self.database,
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)
self.repository = Repository.load(architecture, configuration, self.database, report=report,
refresh_pacman_database=refresh_pacman_database)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -17,16 +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/>.
#
import shutil
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
@ -35,18 +33,6 @@ class ApplicationRepository(ApplicationProperties):
repository control class
"""
def on_result(self, result: Result) -> None:
"""
generate report and sync to remote server
Args:
result(Result): build result
Raises:
NotImplementedError: not implemented method
"""
raise NotImplementedError
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
"""
run all clean methods. Warning: some functions might not be available under non-root
@ -54,7 +40,7 @@ class ApplicationRepository(ApplicationProperties):
Args:
cache(bool): clear directory with package caches
chroot(bool): clear build chroot
manual(bool): clear directory with manually added packages
manual(bool): clear directory with manually added packages' bases
packages(bool): clear directory with built packages
pacman(bool): clear directory with pacman databases
"""
@ -69,6 +55,18 @@ class ApplicationRepository(ApplicationProperties):
if pacman:
self.repository.clear_pacman()
def on_result(self, result: Result) -> None:
"""
generate report and sync to remote server
Args:
result(Result): build result
Raises:
NotImplementedError: not implemented method
"""
raise NotImplementedError
def sign(self, packages: Iterable[str]) -> None:
"""
sign packages and repository
@ -85,38 +83,35 @@ class ApplicationRepository(ApplicationProperties):
if archive.filepath is None:
self.logger.warning("filepath is empty for %s", package.base)
continue # avoid mypy warning
src = self.repository.paths.repository / archive.filepath
dst = self.repository.paths.packages / archive.filepath
shutil.copy(src, dst)
# run generic update function
self.update([])
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)
local = Package.from_build(cache_dir, self.architecture, None)
packages = set(probe.packages.keys()).difference(local.packages.keys())
return list(packages)
@ -128,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
@ -141,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
@ -150,17 +147,17 @@ class ApplicationRepository(ApplicationProperties):
process_update(packages, build_result)
# process manual packages
tree = Tree.load(updates, self.repository.paths, self.database)
for num, level in enumerate(tree.levels()):
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
@ -170,24 +167,17 @@ 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 = {}
if aur:
updates.update({package.base: package for package in self.repository.updates_aur(filter_packages, vcs=vcs)})
if local:
updates.update({package.base: package for package in self.repository.updates_local()})
updates.update({package.base: package for package in self.repository.updates_local(vcs=vcs)})
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())]
for package in updated_packages:
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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -32,14 +32,17 @@ from ahriman.application.handlers.remove import Remove
from ahriman.application.handlers.remove_unknown import RemoveUnknown
from ahriman.application.handlers.restore import Restore
from ahriman.application.handlers.search import Search
from ahriman.application.handlers.service_updates import ServiceUpdates
from ahriman.application.handlers.setup import Setup
from ahriman.application.handlers.shell import Shell
from ahriman.application.handlers.sign import Sign
from ahriman.application.handlers.status import Status
from ahriman.application.handlers.status_update import StatusUpdate
from ahriman.application.handlers.structure import Structure
from ahriman.application.handlers.triggers import Triggers
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
from ahriman.application.handlers.update import Update
from ahriman.application.handlers.users import Users
from ahriman.application.handlers.validate import Validate
from ahriman.application.handlers.versions import Versions
from ahriman.application.handlers.web import Web

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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,16 +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, args.without_dependencies)
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)
result = application.update(packages)
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False)
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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,15 +52,15 @@ 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 backup
extract paths to back up
Args:
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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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=False, separator=" = ")
ConfigurationPrinter(section, values).print(verbose=not args.secure, separator=" = ")

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -17,17 +17,15 @@
# 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
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
from ahriman.core.log import Log
from ahriman.models.repository_paths import RepositoryPaths
@ -44,6 +42,7 @@ class Handler:
be called directly. The recommended way is to call ``execute`` class method, e.g.::
>>> from ahriman.application.handlers import Add
>>>
>>> Add.execute(args)
"""
@ -51,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
@ -59,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
@ -82,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
@ -94,9 +93,11 @@ class Handler:
bool: True on success, False otherwise
"""
try:
configuration = Configuration.from_path(args.configuration, architecture, args.quiet)
configuration = Configuration.from_path(args.configuration, architecture)
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
@ -106,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
@ -135,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
@ -145,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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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,16 +47,15 @@ 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:
patch = Patch.patch_create_from_function(args.variable, args.patch)
Patch.patch_set_create(application, args.package, patch)
elif args.action == Action.Update and args.variable is None:
package_base, patch = Patch.patch_create_from_diff(args.package, args.track)
package_base, patch = Patch.patch_create_from_diff(args.package, architecture, args.track)
Patch.patch_set_create(application, package_base, patch)
elif args.action == Action.List:
Patch.patch_set_list(application, args.package, args.variable, args.exit_code)
@ -66,30 +63,31 @@ class Patch(Handler):
Patch.patch_set_remove(application, args.package, args.variable)
@staticmethod
def patch_create_from_diff(sources_dir: Path, 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
track(List[str]): track files which match the glob before creating the patch
architecture(str): repository architecture
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)
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
@ -115,17 +113,16 @@ 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)
Patch.check_if_empty(exit_code, not patches)
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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 application.repository.packages()
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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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,15 +40,14 @@ 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()
if args.dry_run:
for package in sorted(unknown_packages):
StringPrinter(package).print(False)
StringPrinter(package).print(verbose=False)
return
application.remove(unknown_packages)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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}
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)
@ -64,10 +66,10 @@ class Search(Handler):
for packages_list in (official_packages_list, aur_packages_list):
# keep sorting by packages source
for package in Search.sort(packages_list, args.sort_by):
AurPrinter(package).print(args.info)
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

@ -0,0 +1,61 @@
#
# 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/>.
#
import argparse
from ahriman import version
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.package import Package
class ServiceUpdates(Handler):
"""
service updates handler
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
Args:
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
"""
application = Application(architecture, configuration, report=report)
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}"
# technically we would like to compare versions, but it is fine to raise an exception in case if locally
# installed package is newer than in AUR
same_version = remote.version == local_version
if same_version:
return
UpdatePrinter(remote, local_version).print(verbose=True, separator=" -> ")
ServiceUpdates.check_if_empty(args.exit_code, not same_version)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -21,12 +21,12 @@ import argparse
from pathlib import Path
from pwd import getpwuid
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.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,21 +55,21 @@ 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.include,
configuration.repository_paths)
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, application.repository.paths)
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
Setup.executable_create(application.repository.paths, args.build_command, architecture)
Setup.configuration_create_devtools(args.build_command, architecture, args.from_configuration,
Setup.configuration_create_devtools(args.build_command, architecture, args.from_configuration, args.mirror,
args.multilib, args.repository, application.repository.paths)
Setup.configuration_create_sudo(application.repository.paths, args.build_command, architecture)
application.repository.repo.init()
# lazy database sync
application.repository.pacman.handle # pylint: disable=pointless-statement
@staticmethod
def build_command(root: Path, prefix: str, architecture: str) -> Path:
@ -78,7 +77,7 @@ class Setup(Handler):
generate build command name
Args:
root(Path): root directory for the build command (must be root of the reporitory)
root(Path): root directory for the build command (must be root of the repository)
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture
@ -89,7 +88,7 @@ class Setup(Handler):
@staticmethod
def configuration_create_ahriman(args: argparse.Namespace, architecture: str, repository: str,
include_path: Path, paths: RepositoryPaths) -> None:
root: Configuration) -> None:
"""
create service specific configuration
@ -97,45 +96,57 @@ class Setup(Handler):
args(argparse.Namespace): command line args
architecture(str): repository architecture
repository(str): repository name
include_path(Path): path to directory with configuration includes
paths(RepositoryPaths): repository paths instance
root(Configuration): root configuration instance
"""
configuration = Configuration()
section = Configuration.section_name("build", architecture)
build_command = Setup.build_command(paths.root, args.build_command, architecture)
build_command = Setup.build_command(root.repository_paths.root, args.build_command, architecture)
configuration.set_option(section, "build_command", str(build_command))
configuration.set_option("repository", "name", repository)
if args.build_as_user is not None:
configuration.set_option(section, "makechrootpkg_flags", f"-U {args.build_as_user}")
section = Configuration.section_name("alpm", architecture)
if args.mirror is not None:
configuration.set_option(section, "mirror", args.mirror)
if not args.multilib:
repositories = filter(lambda r: r != "multilib", root.getlist("alpm", "repositories"))
configuration.set_option(section, "repositories", " ".join(repositories))
section = Configuration.section_name("sign", architecture)
if args.sign_key is not None:
section = Configuration.section_name("sign", architecture)
configuration.set_option(section, "target", " ".join([target.name.lower() for target in args.sign_target]))
configuration.set_option(section, "key", args.sign_key)
section = Configuration.section_name("web", architecture)
if args.web_port is not None:
section = Configuration.section_name("web", architecture)
configuration.set_option(section, "port", str(args.web_port))
if args.web_unix_socket is not None:
configuration.set_option(section, "unix_socket", str(args.web_unix_socket))
target = include_path / "setup-overrides.ini"
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,
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
multilib(bool): add or do not multilib repository
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
"""
@ -144,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
@ -157,26 +168,40 @@ class Setup(Handler):
if multilib:
configuration.set_option("multilib", "Include", str(Setup.MIRRORLIST_PATH))
# override Include option to Server in case if mirror option set
if mirror is not None:
for section in filter(lambda s: s != "options", configuration.sections()):
if configuration.get(section, "Include", fallback=None) != str(Setup.MIRRORLIST_PATH):
continue
configuration.remove_option(section, "Include")
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)
@staticmethod
def configuration_create_makepkg(packager: str, paths: RepositoryPaths) -> None:
def configuration_create_makepkg(packager: str, makeflags_jobs: bool, paths: RepositoryPaths) -> None:
"""
create configuration for makepkg
Args:
packager(str): packager identifier (e.g. name, email)
makeflags_jobs(bool): set MAKEFLAGS variable to number of cores
paths(RepositoryPaths): repository paths instance
"""
content = f"PACKAGER='{packager}'\n"
if makeflags_jobs:
content += """MAKEFLAGS="-j$(nproc)"\n"""
uid, _ = paths.root_owner
home_dir = Path(getpwuid(uid).pw_dir)
(home_dir / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
(home_dir / ".makepkg.conf").write_text(content, encoding="utf8")
@staticmethod
def configuration_create_sudo(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
@ -190,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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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,15 +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)
local_variables = {"architecture": architecture, "configuration": configuration}
if args.code is None:
code.interact(local=locals())
code.interact(local=local_variables)
else:
code.InteractiveConsole(locals=locals()).runcode(args.code)
code.InteractiveConsole(locals=local_variables).runcode(args.code)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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(args.info)
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(args.info)
PackagePrinter(package, package_status).print(verbose=args.info)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
@ -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

@ -0,0 +1,52 @@
#
# 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/>.
#
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import TreePrinter
from ahriman.core.tree import Tree
class Structure(Handler):
"""
dump repository structure handler
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *, report: bool) -> None:
"""
callback for command line
Args:
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
"""
application = Application(architecture, configuration, report=report)
packages = application.repository.packages()
tree = Tree.resolve(packages)
for num, level in enumerate(tree):
TreePrinter(num, level).print(verbose=True, separator=" ")

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