Compare commits

...

120 Commits

Author SHA1 Message Date
9c1e9ecbdc Release 2.14.1 2024-09-04 22:01:04 +03:00
4b2f6bbee9 bug: fix removal of the packages
It has been broken since reporter improvements, because it effectivelly
1) didn't call remove functions in database
2) used empty repository identifier for web service

With those changes it also raises exception when you try to call id on
empty identifier
2024-09-04 21:50:33 +03:00
fd8c8a00d0 chore: small contributing guide update 2024-09-04 21:49:31 +03:00
eaf1984eb3 refactor: fix some IDE warnings 2024-09-04 21:49:31 +03:00
794dddccd9 build: update pytest configuration to suppress deprecation warnings 2024-09-04 21:49:31 +03:00
7bd7f95f76 Release 2.14.0 2024-08-23 14:37:05 +03:00
375374c396 docs: improve waiter classes docs 2024-08-23 14:33:07 +03:00
d1ad5ecc11 feat: add ability to suppress git hints
It can be done by setting options in command. The commit author/email is
also now using this logic
2024-08-23 14:33:07 +03:00
1eb4d8e47f feat: add blacklisted paths to implicit dependencies processing
It has been found that in some cases additional packages have been added
as dependencies, like usr/share/applications, usr/lib/cmake, etc

This commit adds an ability to blacklist specific paths from processing
2024-08-23 14:33:07 +03:00
0861548b56 docs: split faq into multiple files 2024-08-20 16:44:40 +03:00
e9e4172063 feat: add support of pam authentication
Add naive implementation of user password check by calling su command.
Also change some authentication method to require username to be string
instead of optional string
2024-08-20 16:44:40 +03:00
beb6156795 fix: print current and updated version correctly
The issue appears in case if versions ar the same (e.g. rebuild); in
this case printer doesn't increment version as builder does.

Also util has been renamed to utils, keeping backward compatibiltiy
2024-08-16 16:24:11 +03:00
dbfb460557 feat: optimize archive reading
Instead of trying to load every database and look for files, this commit
introduces the optimization in which, the service loads packages first,
groups them by database and load files later.

In some cases it significantly descreases times for loading files
2024-08-14 17:07:10 +03:00
f7f76c4119 fix: explicitly process list of packages
Small workaround to remove debug packages from being processed
2024-08-14 17:07:10 +03:00
88ee300b9e fix: remove trailit slash when loading packages files from a database 2024-08-14 17:07:10 +03:00
6f30c687c2 fix: skip debug packages as well 2024-08-14 17:07:10 +03:00
c023ebe165 docs: update documentation for implicit dependencies resolution 2024-08-14 17:07:10 +03:00
54b99cacfd feat: remove excess dependencies leaves (#128)
This mr improves implicit dependencies processing by reducing tree leaves by using the following algorithm:

* remove paths which belong to any base package
* remove packages which are (opt)dependencies of one of the package which provides same path. It also tries to handle circular dependencies by excluding them from being "satisfied"
* remove packages which are already satisfied by any children path
2024-08-14 17:07:10 +03:00
4f5166ff25 feat: improve lock mechanisms
* improve lock mechanisms

* use /run/ahriman for sockett

* better water
2024-08-14 17:07:10 +03:00
c8afcbf36a feat: implement local reporter mode (#126)
* implement local reporter mode

* simplify watcher class

* review changes

* do not update unknown status

* allow empty key patches via api

* fix some pylint warnings in tests
2024-08-14 17:07:10 +03:00
2b9880bd3c feat: allow to use simplified keys for context
Initial implementation requires explicit context key name to be set.
Though it is still useful sometimes (e.g. if there should be two
variables with the same type), in the most used scenarios internally
only type is required. This commit extends set and get methods to allow
to construct ContextKey from type directly

Also it breaks old keys, since - in order to reduce amount of possible
mistakes - internal classes uses this generation method
2024-08-14 17:07:10 +03:00
3be5cdafe8 feat: add abillity to check broken dependencies (#122)
* implement elf dynamic linking check

* load local database too in pacman wrapper
2024-08-14 17:07:10 +03:00
668be41c3e type: drop MiddlewareType in favour of Middleware builtin 2024-08-14 17:07:10 +03:00
3353daec6d type: fix mypy warn for fresh unixsocket release 2024-08-14 17:07:10 +03:00
eef4d2dd98 type: remove another unused mypy directive 2024-08-14 17:07:10 +03:00
b15161554e build: use requests-unixsocket2 fork
Since requests-2.32.0, the http+unix url scheme is brokek, check
https://github.com/msabramo/requests-unixsocket/issues/73 for more
details
2024-08-14 17:07:10 +03:00
bb4a0d75fc Release 2.13.8 2024-05-12 11:53:19 +03:00
bca0df41d1 fix: drop integrity check for javascript
It has been added to improve security, however, it changes over time for
no reason ¯\_(ツ)_/¯ I guess either cdn was hacked or fuck js
2024-05-12 11:49:12 +03:00
07b77be6b8 Release 2.13.7 2024-05-09 13:26:40 +03:00
2b33510ada fix: parse array variable from command 2024-05-09 13:21:42 +03:00
6d05389639 Release 2.13.6 2024-05-05 21:59:30 +03:00
daf9841717 fix: update integrity checksums for momentjs and daterangepicker 2024-05-05 21:17:30 +03:00
0d243a781a refactor: update code to the latest python (3.12+) 2024-05-05 21:17:30 +03:00
cf2e66a934 fix: remove debug packages together with normal ones (#124) 2024-05-05 21:17:30 +03:00
f01f35238d Release 2.13.5 2024-04-04 13:33:03 +03:00
d30d512eb6 fix: update Repo.init to the latest pacman release 2024-04-04 13:16:05 +03:00
0437f90e5a build: install base-devel package 2024-04-04 13:16:03 +03:00
3cab65855a fix: lazy web component initialization
In some cases (probably slow internet) in place initialization can cause
exception, because elements are not available yet. This commit moves
events initialization to $()
2024-04-04 13:14:17 +03:00
ecfb615f97 feat: add ability to disable debug packages distribution
The feature is implemented as supplying !debug option to makepkg when
generating package list. In this case debug packages still will be
built, however, they will not be added to the repository
2024-04-04 13:14:17 +03:00
243983ee64 docs: update docs 2024-02-10 03:12:09 +02:00
812c03d1eb Release 2.13.4 2024-02-09 17:47:01 +02:00
01597c531b fix: return only built packages from task
Since the last updates makepkg --packagelist also adds debug packages
which causes errors
2024-02-09 17:37:50 +02:00
4fec42eac8 refactor: rename packages http methods to own package
docs: update docs import
2024-01-22 02:20:11 +02:00
7574b8e5ce Release 2.13.3 2024-01-13 01:24:30 +02:00
0f2e7f45da fix: replace logo and name in title to just icon 2024-01-12 01:25:46 +02:00
5956a8720b Release 2.13.2 2024-01-08 22:48:55 +02:00
8dd4ced5e9 fix: report only unique result entries
since builder intro the triggers are called with merged result, thus it
would lead to duplicated callouts
2024-01-08 22:46:42 +02:00
6361c41f76 Release 2.13.1 2024-01-08 21:17:35 +02:00
270084bb39 fix: do not raise 404 in case of unknown package on patches endpoints
Previous improvements raise 404 error in case if no packages were found
for patches endpoints. However, in case of multirepo setup this feature
doesn't work properly because package can be located in any other
repository different from default
2024-01-08 14:32:40 +02:00
f89a5252de build: pass ssh agent to tox release env 2024-01-08 14:22:53 +02:00
8cafdb52e5 Release 2.13.0 2024-01-05 22:48:03 +02:00
203ebad817 ci: explicit isolated build for old ubuntu tox 2024-01-05 22:47:28 +02:00
9f471d11a7 docs: add comments to configuration 2024-01-05 22:24:37 +02:00
2ea8a4a07f test: add pytlint imports plugin and fix errors 2024-01-05 19:52:51 +02:00
856bbc30d4 refactor: fix pylint warnings in tests 2024-01-05 16:40:38 +02:00
c88f97c36e refactor: simplify lock processing in worker trigger 2024-01-05 16:11:32 +02:00
174d7578a0 refactor: split Path elements to / and first directory 2024-01-05 15:22:46 +02:00
8b26035fc9 docs: url spelling 2024-01-05 12:37:23 +02:00
fc9391dd5f chore: add index page recipe 2024-01-05 12:34:55 +02:00
ed7ed5d5d9 feat: read username if email is not available for oauth provider
Also add recipe for OAuth with GitHub setup
2024-01-05 12:28:31 +02:00
9e1cf3cde7 refactor: use root RequestException instead of the one from subpackage 2024-01-05 11:48:38 +02:00
08086e8ba8 fix: safe urls for packages
String catenation used for url generators didn't encode package names
which could lead to missing data in case if e.g. there is slash (/) in
package name
2024-01-03 14:28:20 +02:00
1a61da7ab9 refactor: remove custom access logger
It is fine when application is able to log request, however, normally it
produces a lot noise, which has been handled by adding special logger.
However, nowadays it requires a lot endpoints to be filtered and doesn't
provide any choice.

Instead of it lets disable access logger by default and let users decide
do they need or not to see access log messages
2024-01-03 12:18:50 +02:00
f4dcbbde18 chore: copyright update 2024-01-03 12:09:10 +02:00
7aba67186f feat: raise 404 in case if package is unknown for logs and patches 2024-01-03 12:09:10 +02:00
8635ee8953 feat: threadsafe services
In the most cases it was enough to just add lock. In case of worker
trigger, since there is atomic operation on timer, it was also required
to add queue (coz python doesn't have atomics)
2024-01-03 12:09:10 +02:00
2d42424477 feat: add workers autodicsovery feature (#121)
* add workers autodicsovery feature

* suppress erros while retrieving worker list

* update recipes

* fix tests and update docs

* filter health checks

* ping based workers
2024-01-03 02:25:24 +02:00
fdf7a36271 chore: make yamll style consistent 2023-12-29 03:57:13 +02:00
e9116741a5 refactor: fix warnings and typos, cleanup code 2023-12-28 16:53:45 +02:00
1a2327cefd build: install aiohttp-cors from aur 2023-12-28 16:05:48 +02:00
29c8c4bfd7 chore: add daemon unit 2023-12-28 15:24:14 +02:00
e40b773029 test: add tests to check args list
Some parsers are shared between different subcommands. It causes errors
when wwe add new arguments to one of them. This commit adds some tests
to cover those cases (except for well-known differencies)
2023-12-27 15:01:07 +02:00
ee3ccf70ac refactor: use AppKey's instead of string identifiers for web application 2023-12-27 13:53:23 +02:00
e4b22fd620 feat: allow to run daemon mode with split packages check (#120) 2023-12-27 03:05:44 +02:00
48344f759d feat: add healh-check like endpoint, support of healthcheck in docker compose 2023-12-21 17:49:35 +02:00
290e6e7670 docs: add reference how to copy package 2023-12-21 02:14:27 +02:00
e0e4da6669 fix: drop debug toolbar 2023-12-20 16:26:14 +02:00
0e2ad3325c docs: docs review 2023-12-20 10:00:12 +02:00
28589cb122 chore: add configuration recipes 2023-12-20 10:00:12 +02:00
93793b6424 build: drop makefile
It has been a while since we moved to tox. Lets also drop makefile since
it seems to be useless with tox
2023-12-17 02:26:52 +02:00
b3ce545517 docs: restore docs for the view 2023-12-15 16:17:19 +02:00
e51d91740d feat: add ability to disable specific routes (#119) 2023-12-15 14:34:03 +02:00
5ddc08fce7 feat: add ability to run build process to remote instances (#118) 2023-12-13 15:38:51 +02:00
f2f6f6df70 fix: correct url for update requests in remote-call trigger 2023-12-11 15:43:28 +02:00
2760b36977 feat: changes screen implementation (#117)
Add support of changes generation. Changes will be generated (unless explicitly asked not to) automatically during check process (i.e. `repo-update --dry-run` and aliases) and uploaded to the remote server. Changes can be reviewed either by web interface or by special subcommands.

Changes will be automatically cleared during next successful build
2023-11-30 14:56:41 +02:00
a689448854 fix: use event instead of chained timer for daemon
Old solution causes amount of thread to be growing as well as stack is
increased during each iteration. Instead of cycle-free implementation,
this commit just uses while cycle
2023-11-30 13:40:59 +02:00
aef3cb95bc type: update to the typed aiohttp release 2023-11-23 15:35:38 +02:00
d72677aa29 feat: forbid form data in html
It has been a while since all pages have moved to json instead of form
data, except for login page. This commit changes login to json data
instead of form one
2023-11-16 16:42:27 +02:00
82d1be52a8 feat: add separated web client for ahriman web services 2023-11-14 16:41:33 +02:00
7536d6bb82 docs: move timeout settings from web to status group 2023-11-13 16:53:16 +02:00
b050c409cf Release 2.12.2 2023-11-13 12:26:00 +02:00
d77cf7c4bb style: highligh __del__ methods in formatting 2023-11-13 12:16:30 +02:00
e03fcbfab5 fix: fix some security issues 2023-11-12 22:22:05 +02:00
62dd77317d feat: add separated switch for status reporting 2023-11-11 15:18:17 +02:00
95056cfbe7 feat: extend result class 2023-11-10 17:09:01 +02:00
2d31a415ce fix: pass packagers object insteaed of string during rebuild 2023-11-08 16:05:27 +02:00
5cbeec40f8 build: rename push action to release 2023-11-06 23:25:29 +02:00
4a644fc80f Release 2.12.1 2023-11-06 16:31:56 +02:00
d1aeeda975 fix: show unique upstream urls only 2023-11-06 16:29:18 +02:00
53aedbb9aa Release 2.12.0 2023-11-06 15:50:11 +02:00
c941226a9b feat: add upstream and aur urls to package info modal 2023-11-06 15:46:32 +02:00
69cbbfce65 feat: serve favicon in root 2023-11-06 15:46:32 +02:00
52d69fa9f6 build: remove spaces from names 2023-11-06 14:04:15 +02:00
279ac785a1 docs: correct crossreference in docs 2023-11-06 14:04:15 +02:00
f51b8e2358 refactor: replace enum with intenum and strenum 2023-11-05 13:26:11 +02:00
b116e6fa07 feat: allow to pass repository identifier to all database methods 2023-11-05 13:26:11 +02:00
7b667c8157 build: remove unused files from tarball 2023-11-05 13:26:11 +02:00
c51463d36b chore: add metthod definition order plugin to pylint
Also reorder some methods to fix errors
2023-11-05 13:26:11 +02:00
79947af013 feat: read old web sections 2023-11-04 13:34:53 +02:00
e6d6cb006b feat: improve small screens views
also fix case when invalid repository id has been sent
2023-11-01 16:38:53 +02:00
f553cc059a fix: reset logs window manually 2023-10-31 12:33:57 +02:00
c99d4f6f0f fix: correct month serialization 2023-10-31 10:13:25 +02:00
554827cc57 feat: add patch controls to web, review web, enrich info tab (#115)
* add ability to specify one-time patch on package addition

* support vars in interface
2023-10-29 23:41:20 +02:00
8524f1eb20 refactor: drop _check_output class attribute 2023-10-23 02:24:53 +03:00
4b6feb9ae6 refactor: add ability to pass anything as environment variable to build task 2023-10-23 02:24:41 +03:00
ad901067d8 fix: make toggle button works on small displays 2023-10-21 15:18:09 +03:00
4c20897e14 fix: write new line for telegram index 2023-10-21 03:56:48 +03:00
f8cbd07259 fix: do not automatically add unknown local packages
Instead of automatic package addition now it is required to add package
manually after clone. Less magic, plus would allow to use caches for
multi-repo setup (see #109)
2023-10-21 01:33:12 +03:00
16ad96d8c6 feat: add ability to run multiple commands on success 2023-10-19 04:34:49 +03:00
6bd1636bfa feat: allow to use single web instance for all repositories (#114)
* Allow to use single web instance for any repository

* some improvements

* drop includes from user home directory, introduce new variables to docker

The old solution didn't actually work as expected, because devtools
configuration belongs to filesystem (as well as sudo one), so it was
still required to run setup command.

In order to handle additional repositories, the POSTSETUP and PRESETUP
commands variables have been introduced. FAQ has been updated as well

* raise 404 in case if repository is unknown
2023-10-17 03:58:50 +03:00
625 changed files with 32097 additions and 14154 deletions

View File

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

View File

@ -1 +1,3 @@
skips: ['B404', 'B603']
skips:
- B404
- B603

View File

@ -9,5 +9,4 @@ Put `closes #ISSUE` in case if the pull requests solves one of the opened issues
### Checklist
- [ ] Tests to cover new code
- [ ] `make check` passed
- [ ] `make tests` passed
- [ ] `tox` passed

View File

@ -2,7 +2,8 @@ name: Docker image
on:
push:
branches: [ master ]
branches:
- master
tags:
- '*'
- '!*rc*'

View File

@ -24,8 +24,13 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
filter: 'Release \d+\.\d+\.\d+'
- name: Install dependencies
uses: ConorMacBride/install-package@v1.1.0
with:
apt: tox
- name: Create archive
run: make archive
run: tox -e archive
env:
VERSION: ${{ steps.version.outputs.VERSION }}
@ -35,7 +40,7 @@ jobs:
body: |
${{ steps.changelog.outputs.compareurl }}
${{ steps.changelog.outputs.changelog }}
files: ahriman-*-src.tar.xz
files: dist/ahriman-*.tar.gz
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -6,43 +6,46 @@ set -ex
[[ $1 = "minimal" ]] && MINIMAL_INSTALL=1
# install dependencies
echo -e '[arcanisrepo]\nServer = http://repo.arcanis.me/$arch\nSigLevel = Never' | tee -a /etc/pacman.conf
echo -e '[arcanisrepo]\nServer = https://repo.arcanis.me/$arch\nSigLevel = Never' | tee -a /etc/pacman.conf
# refresh the image
pacman --noconfirm -Syu
pacman -Syu --noconfirm
# main dependencies
pacman --noconfirm -Sy base-devel devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo python-systemd sudo
pacman -Sy --noconfirm devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo python-systemd sudo
# make dependencies
pacman --noconfirm -Sy python-build python-flit python-installer python-wheel
pacman -Sy --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel
# optional dependencies
if [[ -z $MINIMAL_INSTALL ]]; then
# VCS support
pacman --noconfirm -Sy breezy darcs mercurial subversion
pacman -Sy --noconfirm breezy darcs mercurial subversion
# web server
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
pacman -Sy --noconfirm python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
# additional features
pacman --noconfirm -Sy gnupg python-boto3 rsync
pacman -Sy --noconfirm gnupg python-boto3 rsync
fi
# FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container
cp "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
# create fresh tarball
make VERSION=1.0.0 archlinux # well, it does not really matter which version we will put here
tox -e archive
# run makepkg
mv ahriman-*-src.tar.xz package/archlinux
mv dist/ahriman-*.tar.gz package/archlinux
chmod +777 package/archlinux # because fuck you that's why
cd package/archlinux
sudo -u nobody -- makepkg -cf --skipchecksums --noconfirm
pacman --noconfirm -U ahriman-1.0.0-1-any.pkg.tar.zst
sudo -u nobody -- makepkg --packagelist | grep -v -- -debug- | pacman -U --noconfirm -
# create machine-id which is required by build tools
systemd-machine-id-setup
# remove unused dependencies
pacman -Qdtq | pacman -Rscn --noconfirm -
# initial setup command as root
[[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080")
ahriman -a x86_64 -r "github" service-setup --packager "ahriman bot <ahriman@example.com>" "${WEB_ARGS[@]}"
# validate configuration
ahriman service-config-validate --exit-code
# enable services
systemctl enable ahriman-web@x86_64-github
systemctl enable ahriman-web
systemctl enable ahriman@x86_64-github.timer
if [[ -z $MINIMAL_INSTALL ]]; then
# run web service (detached)
@ -50,7 +53,7 @@ if [[ -z $MINIMAL_INSTALL ]]; then
WEB_PID=$!
fi
# add the first package
sudo -u ahriman -- ahriman package-add --now ahriman
sudo -u ahriman -- ahriman --log-handler console package-add --now ahriman
# check if package was actually installed
test -n "$(find "/var/lib/ahriman/repository/github/x86_64" -name "ahriman*pkg*")"
# run package check

View File

@ -2,9 +2,11 @@ name: Setup
on:
push:
branches: [ master ]
branches:
- master
pull_request:
branches: [ master ]
branches:
- master
jobs:
run-setup-minimal:

View File

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

View File

@ -2,9 +2,11 @@ name: Tests
on:
push:
branches: [ master ]
branches:
- master
pull_request:
branches: [ master ]
branches:
- master
jobs:
run-tests:

View File

@ -67,7 +67,7 @@ ignored-modules=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
init-hook='import sys; sys.path.append("pylint_plugins")'
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use, and will cap the count on Windows to
@ -81,7 +81,10 @@ limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=
load-plugins=pylint.extensions.docparams,
pylint.extensions.bad_builtin,
definition_order,
import_order,
# Pickle collected data for later comparisons.
persistent=yes
@ -129,6 +132,8 @@ attr-naming-style=snake_case
# style.
#attr-rgx=
bad-functions=print,
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
@ -227,7 +232,7 @@ name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
no-docstring-rgx=
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.

View File

@ -1,17 +1,9 @@
version: 2
formats:
- pdf
build:
os: ubuntu-20.04
tools:
python: "3.11"
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true
python: "3.12"
python:
install:
@ -21,3 +13,11 @@ python:
- docs
- s3
- web
formats:
- pdf
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true

View File

@ -19,14 +19,14 @@ There are some strict limitation for suggested pull requests:
### Code formatting
In order to resolve all difficult cases the `autopep8` is used. You can perform formatting at any time by running `make check` or running `autopep8` command directly.
In order to resolve all difficult cases the `autopep8` is used. You can perform formatting at any time by running `tox` or running `autopep8` command directly.
### Code style
Again, the most checks can be performed by `make check` command, though some additional guidelines must be applied:
Again, the most checks can be performed by `tox` 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.
* Correct way to document function, if section is empty, e.g. no notes or there are no args, it should be omitted:
* Correct way to document function (if a section is empty, e.g. no notes or there are no args, it should be omitted) is the following:
```python
def foo(argument: str, *, flag: bool = False) -> int:
@ -43,7 +43,8 @@ Again, the most checks can be performed by `make check` command, though some add
flag(bool, optional): a flag (Default value = False)
Returns:
int: result
int: result with
long description
Raises:
RuntimeError: a local function error occurs
@ -64,7 +65,7 @@ Again, the most checks can be performed by `make check` command, though some add
```python
class Clazz(BaseClazz):
"""
brand-new implementation of ``BaseClazz``
brand-new implementation of :class:`BaseClazz`
Attributes:
CLAZZ_ATTRIBUTE(int): (class attribute) a brand-new class attribute
@ -92,7 +93,7 @@ Again, the most checks can be performed by `make check` command, though some add
* 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.
* `classmethod` should (almost) always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead.
* Recommended order of function definitions in class:
```python
@ -121,15 +122,17 @@ Again, the most checks can be performed by `make check` command, though some add
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.
Methods inside one group should be ordered alphabetically, the only exceptions are `__init__` (`__post_init__` for dataclasses), `__new__` and `__del__` methods 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.
For the most cases there is custom `pylint` plugin, which performs checks automatically.
* 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.
* The code must follow the exception safety, unless it is explicitly asked by end user. It means that most exceptions must be handled and printed to log, no other actions must be done (e.g. raising another exception).
* The most (expected) exceptions must be handled and printed to log, allowing service to continue work. However, fatal and (in some cases) unexpected exceptions may lead to the application termination.
* Exceptions without parameters should be raised without parentheses, e.g.:
```python
@ -167,7 +170,7 @@ Again, the most checks can be performed by `make check` command, though some add
* No global variable is allowed outside of `ahriman` module. `ahriman.core.context` is also special case.
* Single quotes are not allowed. The reason behind this restriction is the fact that docstrings must be written by using double quotes only, and we would like to make style consistent.
* If your class writes anything to log, the `ahriman.core.log.LazyLogging` trait must be used.
* Web API methods must be documented by using `aiohttp_apispec` library. Schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
* Web API methods must be documented by using `aiohttp_apispec` library. The schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
```python
import aiohttp_apispec
@ -191,6 +194,7 @@ Again, the most checks can be performed by `make check` command, though some add
class Foo(BaseView):
POST_PERMISSION = ...
ROUTES = ...
@aiohttp_apispec.docs(
tags=["Tag"],
@ -202,6 +206,7 @@ Again, the most checks can be performed by `make check` command, though some add
400: {"description": "Bad data is supplied", "schema": ErrorSchema}, # exception raised by this method
401: {"description": "Authorization required", "schema": ErrorSchema}, # should be always presented
403: {"description": "Access is forbidden", "schema": ErrorSchema}, # should be always presented
404: {"description": "Repository is unknown", "schema": ErrorSchema}, # include if BaseView.service() method is called
500: {"description": "Internal server error", "schema": ErrorSchema}, # should be always presented
},
security=[{"token": [POST_PERMISSION]}],
@ -215,7 +220,30 @@ Again, the most checks can be performed by `make check` command, though some add
* It is allowed to change web API to add new fields or remove optional ones. However, in case of model changes, new API version must be introduced.
* On the other hand, it is allowed to change method signatures, however, it is recommended to add new parameters as optional if possible. Deprecated API can be dropped during major release.
* Enumerations (`Enum` classes) are allowed and recommended. However, it is recommended to use `StrEnum` class if there are from/to string conversions and `IntEnum` otherwise.
### Other checks
The projects also uses typing checks (provided by `mypy`) and some linter checks provided by `pylint` and `bandit`. Those checks must be passed successfully for any open pull requests.
## Developers how to
### Run automated checks
```shell
tox
```
### Generate documentation templates
```shell
tox -e docs
```
### Create release
```shell
tox -m release -- x.y.z
```
The command above will generate documentation, tags, etc., and will push them to GitHub. Other things will be handled by GitHub workflows automatically.

View File

@ -10,6 +10,8 @@ ENV AHRIMAN_OUTPUT=""
ENV AHRIMAN_PACKAGER="ahriman bot <ahriman@example.com>"
ENV AHRIMAN_PACMAN_MIRROR=""
ENV AHRIMAN_PORT=""
ENV AHRIMAN_POSTSETUP_COMMAND=""
ENV AHRIMAN_PRESETUP_COMMAND=""
ENV AHRIMAN_REPOSITORY="aur-clone"
ENV AHRIMAN_REPOSITORY_SERVER=""
ENV AHRIMAN_REPOSITORY_ROOT="/var/lib/ahriman/ahriman"
@ -21,19 +23,20 @@ ENV AHRIMAN_VALIDATE_CONFIGURATION="yes"
## update pacman.conf with multilib
RUN echo "[multilib]" >> "/etc/pacman.conf" && \
echo "Include = /etc/pacman.d/mirrorlist" >> "/etc/pacman.conf"
## install minimal required packages
RUN pacman --noconfirm -Syu binutils fakeroot git make sudo
## refresh packages, install sudo and install packages for building
RUN pacman -Syu --noconfirm sudo && \
pacman -Sy --noconfirm --asdeps fakeroot python-tox
## create build user
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
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-flit 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
RUN pacman -Sy --noconfirm --asdeps devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo && \
pacman -Sy --noconfirm --asdeps base-devel python-build python-flit python-installer python-wheel && \
pacman -Sy --noconfirm --asdeps breezy git mercurial python-aiohttp python-boto3 python-cryptography python-jinja python-systemd rsync subversion && \
runuser -u build -- install-aur-package python-aioauth-client python-webargs python-aiohttp-apispec-git python-aiohttp-cors \
python-aiohttp-jinja2 python-aiohttp-session python-aiohttp-security python-requests-unixsocket2
## FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container
COPY "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
@ -43,8 +46,8 @@ COPY "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
COPY --chown=build . "/home/build/ahriman"
## create package archive and install it
RUN cd "/home/build/ahriman" && \
make VERSION=$(python -c "from src.ahriman import __version__; print(__version__)") archlinux && \
cp ./*-src.tar.xz "package/archlinux" && \
tox -e archive && \
cp ./dist/*.tar.gz "package/archlinux" && \
cd "package/archlinux" && \
runuser -u build -- makepkg --noconfirm --install --skipchecksums && \
cd / && rm -r "/home/build/ahriman"

View File

@ -1,59 +0,0 @@
.PHONY: archive archlinux check clean directory html push specification tests version
.DEFAULT_GOAL := archlinux
PROJECT := ahriman
FILES := AUTHORS CONTRIBUTING.md COPYING Makefile README.md SECURITY.md docs package pyproject.toml src tox.ini web.png
TARGET_FILES := $(addprefix $(PROJECT)/, $(FILES))
IGNORE_FILES := package/archlinux src/.mypy_cache
$(TARGET_FILES) : $(addprefix $(PROJECT), %) : $(addprefix ., %) directory version
@cp -rp $< $@
archive: archive_directory
tar cJf "$(PROJECT)-$(VERSION)-src.tar.xz" "$(PROJECT)"
rm -rf "$(PROJECT)"
archive_directory: $(TARGET_FILES)
rm -fr $(addprefix $(PROJECT)/, $(IGNORE_FILES))
find "$(PROJECT)" -type f -name "*.pyc" -delete
find "$(PROJECT)" -depth -type d -name "__pycache__" -execdir rm -rf {} +
find "$(PROJECT)" -depth -type d -name "*.egg-info" -execdir rm -rf {} +
archlinux: archive
sed -i "s/pkgver=.*/pkgver=$(VERSION)/" package/archlinux/PKGBUILD
check: clean
tox -e check
clean:
find . -type f -name "$(PROJECT)-*-src.tar.xz" -delete
rm -rf "$(PROJECT)"
directory: clean
mkdir "$(PROJECT)"
html: specification
rm -rf docs/html
tox -e docs-html
push: specification archlinux
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/ahriman-architecture.svg package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
git commit -m "Release $(VERSION)"
git tag "$(VERSION)"
git push
git push --tags
specification:
# make sure that old files are removed
find docs -type f -name "$(PROJECT)*.rst" -delete
tox -e docs
tests: clean
tox -e tests
version:
ifndef VERSION
$(error VERSION is required, but not set)
endif
sed -i 's/^__version__ = .*/__version__ = "$(VERSION)"/' src/ahriman/__init__.py

View File

@ -4,39 +4,41 @@
[![setup status](https://github.com/arcan1s/ahriman/actions/workflows/setup.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/setup.yml)
[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/arcan1s/ahriman?label=Docker%20image)](https://hub.docker.com/r/arcan1s/ahriman)
[![CodeFactor](https://www.codefactor.io/repository/github/arcan1s/ahriman/badge)](https://www.codefactor.io/repository/github/arcan1s/ahriman)
[![Documentation Status](https://readthedocs.org/projects/ahriman/badge/?version=latest)](https://ahriman.readthedocs.io/?badge=latest)
[![Documentation Status](https://readthedocs.org/projects/ahriman/badge/?version=latest)](https://ahriman.readthedocs.io)
Wrapper for managing custom repository inspired by [repo-scripts](https://github.com/arcan1s/repo-scripts).
## Features
* Install-configure-forget manager for the very own repository.
* Multi-architecture support.
* Multi architecture and repository support.
* Dependency manager.
* VCS packages support.
* Official repository support.
* Ability to patch AUR packages and even create package from local PKGBUILDs.
* Various rebuild options with ability to automatically bump package version.
* 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).
* 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:
![web interface](web.png)
## Installation and run
For installation details kindly refer to the [documentation](https://ahriman.readthedocs.io/en/latest/setup.html). For application commands it is possible to get information by using `--help`/`help` command or by using man page ([web version](https://ahriman.readthedocs.io/en/latest/command-line.html)).
For installation details kindly refer to the [documentation](https://ahriman.readthedocs.io/en/stable/setup.html). For application commands it is possible to get information by using `--help`/`help` command or by using man page ([web version](https://ahriman.readthedocs.io/en/stable/command-line.html)).
## Configuration
Every available option is described in the [documentation](https://ahriman.readthedocs.io/en/latest/configuration.html).
Every available option is described in the [documentation](https://ahriman.readthedocs.io/en/stable/configuration.html).
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)
## [FAQ](https://ahriman.readthedocs.io/en/stable/faq/index.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/arcanisrepo/x86_64/).
* [Repository index](https://repo.arcanis.me/arcanisrepo/x86_64/).
* [Telegram feed](https://t.me/arcanisrepo).
Do you have any success story? You can [share it](https://github.com/arcan1s/ahriman/issues/new?template=04-discussion.md)!

View File

@ -49,7 +49,10 @@ fi
if [ -n "$AHRIMAN_UNIX_SOCKET" ]; then
AHRIMAN_SETUP_ARGS+=("--web-unix-socket" "$AHRIMAN_UNIX_SOCKET")
fi
[ -n "$AHRIMAN_PRESETUP_COMMAND" ] && eval "$AHRIMAN_PRESETUP_COMMAND"
ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" service-setup "${AHRIMAN_SETUP_ARGS[@]}"
[ -n "$AHRIMAN_POSTSETUP_COMMAND" ] && eval "$AHRIMAN_POSTSETUP_COMMAND"
# validate configuration if set
[ -n "$AHRIMAN_VALIDATE_CONFIGURATION" ] && ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" service-config-validate --exit-code

View File

@ -6,7 +6,7 @@ for PACKAGE in "$@"; do
BUILD_DIR="$(mktemp -d)"
git clone https://aur.archlinux.org/"$PACKAGE".git "$BUILD_DIR"
cd "$BUILD_DIR"
makepkg --noconfirm --install --rmdeps --syncdeps
makepkg --nocheck --noconfirm --install --rmdeps --syncdeps
cd /
rm -r "$BUILD_DIR"
done

View File

@ -12,7 +12,7 @@ Depending on the goal the package can be used in different ways. Nevertheless, i
from ahriman.models.repository_id import RepositoryId
repository_id = RepositoryId("x86_64", "aur-clone")
configuration = Configuration.from_path(Path("/etc/ahriman.ini"), architecture)
configuration = Configuration.from_path(Path("/etc/ahriman.ini"), repository_id)
database = SQLite.load(configuration)
At this point there are ``configuration`` and ``database`` instances which can be used later at any time anywhere, e.g.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 839 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,6 +1,14 @@
ahriman.application.application package
=======================================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.application.application.workers
Submodules
----------
@ -36,6 +44,14 @@ ahriman.application.application.application\_repository module
:no-undoc-members:
:show-inheritance:
ahriman.application.application.updates\_iterator module
--------------------------------------------------------
.. automodule:: ahriman.application.application.updates_iterator
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -0,0 +1,37 @@
ahriman.application.application.workers package
===============================================
Submodules
----------
ahriman.application.application.workers.local\_updater module
-------------------------------------------------------------
.. automodule:: ahriman.application.application.workers.local_updater
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.application.workers.remote\_updater module
--------------------------------------------------------------
.. automodule:: ahriman.application.application.workers.remote_updater
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.application.workers.updater module
------------------------------------------------------
.. automodule:: ahriman.application.application.workers.updater
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.application.application.workers
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -20,6 +20,14 @@ ahriman.application.handlers.backup module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.change module
------------------------------------------
.. automodule:: ahriman.application.handlers.change
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.clean module
-----------------------------------------
@ -100,6 +108,14 @@ ahriman.application.handlers.remove\_unknown module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.repositories module
------------------------------------------------
.. automodule:: ahriman.application.handlers.repositories
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.restore module
-------------------------------------------
@ -108,6 +124,14 @@ ahriman.application.handlers.restore module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.run module
---------------------------------------
.. automodule:: ahriman.application.handlers.run
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.search module
------------------------------------------

View File

@ -20,6 +20,14 @@ ahriman.core.alpm.pacman module
:no-undoc-members:
:show-inheritance:
ahriman.core.alpm.pacman\_database module
-----------------------------------------
.. automodule:: ahriman.core.alpm.pacman_database
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.alpm.repo module
-----------------------------

View File

@ -36,6 +36,14 @@ ahriman.core.auth.oauth module
:no-undoc-members:
:show-inheritance:
ahriman.core.auth.pam module
----------------------------
.. automodule:: ahriman.core.auth.pam
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -100,6 +100,22 @@ ahriman.core.database.migrations.m011\_repository\_name module
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m012\_last\_commit\_sha module
---------------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m012_last_commit_sha
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m013\_dependencies module
----------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m013_dependencies
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -20,6 +20,22 @@ ahriman.core.database.operations.build\_operations module
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.changes\_operations module
-----------------------------------------------------------
.. automodule:: ahriman.core.database.operations.changes_operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.dependencies\_operations module
----------------------------------------------------------------
.. automodule:: ahriman.core.database.operations.dependencies_operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.logs\_operations module
--------------------------------------------------------

View File

@ -0,0 +1,45 @@
ahriman.core.distributed package
================================
Submodules
----------
ahriman.core.distributed.distributed\_system module
---------------------------------------------------
.. automodule:: ahriman.core.distributed.distributed_system
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.distributed.worker\_loader\_trigger module
-------------------------------------------------------
.. automodule:: ahriman.core.distributed.worker_loader_trigger
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.distributed.worker\_trigger module
-----------------------------------------------
.. automodule:: ahriman.core.distributed.worker_trigger
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.distributed.workers\_cache module
----------------------------------------------
.. automodule:: ahriman.core.distributed.workers_cache
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.distributed
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -20,6 +20,14 @@ ahriman.core.formatters.build\_printer module
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.changes\_printer module
-----------------------------------------------
.. automodule:: ahriman.core.formatters.changes_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.configuration\_paths\_printer module
------------------------------------------------------------
@ -60,6 +68,14 @@ ahriman.core.formatters.printer module
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.repository\_printer module
--------------------------------------------------
.. automodule:: ahriman.core.formatters.repository_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.status\_printer module
----------------------------------------------

View File

@ -4,6 +4,14 @@ ahriman.core.http package
Submodules
----------
ahriman.core.http.sync\_ahriman\_client module
----------------------------------------------
.. automodule:: ahriman.core.http.sync_ahriman_client
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.http.sync\_http\_client module
-------------------------------------------

View File

@ -4,14 +4,6 @@ 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
------------------------------------------

View File

@ -20,6 +20,14 @@ ahriman.core.repository.executor module
:no-undoc-members:
:show-inheritance:
ahriman.core.repository.package\_info module
--------------------------------------------
.. automodule:: ahriman.core.repository.package_info
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.repository.repository module
-----------------------------------------

View File

@ -12,6 +12,7 @@ Subpackages
ahriman.core.build_tools
ahriman.core.configuration
ahriman.core.database
ahriman.core.distributed
ahriman.core.formatters
ahriman.core.gitremote
ahriman.core.http
@ -59,6 +60,14 @@ ahriman.core.util module
:no-undoc-members:
:show-inheritance:
ahriman.core.utils module
-------------------------
.. automodule:: ahriman.core.utils
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -12,6 +12,14 @@ ahriman.core.status.client module
:no-undoc-members:
:show-inheritance:
ahriman.core.status.local\_client module
----------------------------------------
.. automodule:: ahriman.core.status.local_client
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.status.watcher module
----------------------------------

View File

@ -36,6 +36,14 @@ ahriman.models.build\_status module
:no-undoc-members:
:show-inheritance:
ahriman.models.changes module
-----------------------------
.. automodule:: ahriman.models.changes
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.context\_key module
----------------------------------
@ -52,6 +60,22 @@ ahriman.models.counters module
:no-undoc-members:
:show-inheritance:
ahriman.models.dependencies module
----------------------------------
.. automodule:: ahriman.models.dependencies
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.filesystem\_package module
-----------------------------------------
.. automodule:: ahriman.models.filesystem_package
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.internal\_status module
--------------------------------------
@ -100,6 +124,14 @@ ahriman.models.package module
:no-undoc-members:
:show-inheritance:
ahriman.models.package\_archive module
--------------------------------------
.. automodule:: ahriman.models.package_archive
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.package\_description module
------------------------------------------
@ -140,6 +172,14 @@ ahriman.models.pkgbuild\_patch module
:no-undoc-members:
:show-inheritance:
ahriman.models.process\_status module
-------------------------------------
.. automodule:: ahriman.models.process_status
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.property module
------------------------------
@ -188,6 +228,14 @@ ahriman.models.result module
:no-undoc-members:
:show-inheritance:
ahriman.models.scan\_paths module
---------------------------------
.. automodule:: ahriman.models.scan_paths
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.sign\_settings module
------------------------------------
@ -236,6 +284,14 @@ ahriman.models.waiter module
:no-undoc-members:
:show-inheritance:
ahriman.models.worker module
----------------------------
.. automodule:: ahriman.models.worker
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -30,6 +30,14 @@ ahriman.web.cors module
:no-undoc-members:
:show-inheritance:
ahriman.web.keys module
-----------------------
.. automodule:: ahriman.web.keys
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.routes module
-------------------------

View File

@ -20,6 +20,22 @@ ahriman.web.schemas.auth\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.build\_options\_schema module
-------------------------------------------------
.. automodule:: ahriman.web.schemas.build_options_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.changes\_schema module
------------------------------------------
.. automodule:: ahriman.web.schemas.changes_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.counters\_schema module
-------------------------------------------
@ -28,6 +44,14 @@ ahriman.web.schemas.counters\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.dependencies\_schema module
-----------------------------------------------
.. automodule:: ahriman.web.schemas.dependencies_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.error\_schema module
----------------------------------------
@ -44,6 +68,14 @@ ahriman.web.schemas.file\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.info\_schema module
---------------------------------------
.. automodule:: ahriman.web.schemas.info_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.internal\_status\_schema module
---------------------------------------------------
@ -100,6 +132,14 @@ ahriman.web.schemas.package\_names\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_patch\_schema module
-------------------------------------------------
.. automodule:: ahriman.web.schemas.package_patch_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_properties\_schema module
------------------------------------------------------
@ -124,6 +164,14 @@ ahriman.web.schemas.package\_status\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.package\_version\_schema module
---------------------------------------------------
.. automodule:: ahriman.web.schemas.package_version_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.pagination\_schema module
---------------------------------------------
@ -132,6 +180,22 @@ ahriman.web.schemas.pagination\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.patch\_name\_schema module
----------------------------------------------
.. automodule:: ahriman.web.schemas.patch_name_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.patch\_schema module
----------------------------------------
.. automodule:: ahriman.web.schemas.patch_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.pgp\_key\_id\_schema module
-----------------------------------------------
@ -172,6 +236,14 @@ ahriman.web.schemas.remote\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.repository\_id\_schema module
-------------------------------------------------
.. automodule:: ahriman.web.schemas.repository_id_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.search\_schema module
-----------------------------------------
@ -196,6 +268,22 @@ ahriman.web.schemas.update\_flags\_schema module
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.versioned\_log\_schema module
-------------------------------------------------
.. automodule:: ahriman.web.schemas.versioned_log_schema
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.schemas.worker\_schema module
-----------------------------------------
.. automodule:: ahriman.web.schemas.worker_schema
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -30,6 +30,22 @@ ahriman.web.views.index module
:no-undoc-members:
:show-inheritance:
ahriman.web.views.static module
-------------------------------
.. automodule:: ahriman.web.views.static
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.status\_view\_guard module
--------------------------------------------
.. automodule:: ahriman.web.views.status_view_guard
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------

View File

@ -0,0 +1,21 @@
ahriman.web.views.v1.distributed package
========================================
Submodules
----------
ahriman.web.views.v1.distributed.workers module
-----------------------------------------------
.. automodule:: ahriman.web.views.v1.distributed.workers
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.v1.distributed
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -0,0 +1,69 @@
ahriman.web.views.v1.packages package
=====================================
Submodules
----------
ahriman.web.views.v1.packages.changes module
--------------------------------------------
.. automodule:: ahriman.web.views.v1.packages.changes
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.packages.dependencies module
-------------------------------------------------
.. automodule:: ahriman.web.views.v1.packages.dependencies
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.packages.logs module
-----------------------------------------
.. automodule:: ahriman.web.views.v1.packages.logs
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.packages.package module
--------------------------------------------
.. automodule:: ahriman.web.views.v1.packages.package
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.packages.packages module
---------------------------------------------
.. automodule:: ahriman.web.views.v1.packages.packages
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.packages.patch module
------------------------------------------
.. automodule:: ahriman.web.views.v1.packages.patch
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.packages.patches module
--------------------------------------------
.. automodule:: ahriman.web.views.v1.packages.patches
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.v1.packages
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -7,6 +7,8 @@ Subpackages
.. toctree::
:maxdepth: 4
ahriman.web.views.v1.distributed
ahriman.web.views.v1.packages
ahriman.web.views.v1.service
ahriman.web.views.v1.status
ahriman.web.views.v1.user

View File

@ -4,26 +4,18 @@ ahriman.web.views.v1.status package
Submodules
----------
ahriman.web.views.v1.status.logs module
ahriman.web.views.v1.status.info module
---------------------------------------
.. automodule:: ahriman.web.views.v1.status.logs
.. automodule:: ahriman.web.views.v1.status.info
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.status.package module
------------------------------------------
ahriman.web.views.v1.status.repositories module
-----------------------------------------------
.. automodule:: ahriman.web.views.v1.status.package
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.v1.status.packages module
-------------------------------------------
.. automodule:: ahriman.web.views.v1.status.packages
.. automodule:: ahriman.web.views.v1.status.repositories
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -0,0 +1,21 @@
ahriman.web.views.v2.packages package
=====================================
Submodules
----------
ahriman.web.views.v2.packages.logs module
-----------------------------------------
.. automodule:: ahriman.web.views.v2.packages.logs
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.v2.packages
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -7,7 +7,7 @@ Subpackages
.. toctree::
:maxdepth: 4
ahriman.web.views.v2.status
ahriman.web.views.v2.packages
Module contents
---------------

View File

@ -1,21 +0,0 @@
ahriman.web.views.v2.status package
===================================
Submodules
----------
ahriman.web.views.v2.status.logs module
---------------------------------------
.. automodule:: ahriman.web.views.v2.status.logs
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.v2.status
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -6,7 +6,7 @@ Package structure
Packages have strict rules of importing:
* ``ahriman.application`` package must not be used anywhere except for itself.
* ``ahriman.application`` package must not be used outside of this package.
* ``ahriman.core`` and ``ahriman.models`` packages don't have any import restriction. Actually we would like to totally restrict importing of ``core`` package from ``models``, but it is impossible at the moment.
* ``ahriman.web`` package is allowed to be imported from ``ahriman.application`` (web handler only, only ``ahriman.web.web`` methods). It also must not be imported globally, only local import is allowed.
@ -19,10 +19,12 @@ Full dependency diagram:
``ahriman.application`` package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from base ``ahriman.application.handlers.Handler`` class.
This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.Handler`` class.
``ahriman.application.application.Application`` (god class) is used for any interaction from parsers with repository. It is divided into multiple traits by functions (package related and repository related) in the same package.
``ahriman.application.application.workers`` package contains specific wrappers for local and remote build processes.
``ahriman.application.ahriman`` contains only command line parses and executes specified ``Handler`` on success, ``ahriman.application.lock.Lock`` is additional class which provides file-based lock and also performs some common checks.
``ahriman.core`` package
@ -31,14 +33,15 @@ This package contains application (aka executable) related classes and everythin
This package contains everything required for the most of application actions and it is separated into several packages:
* ``ahriman.core.alpm`` package controls pacman related functions. It provides wrappers for ``pyalpm`` library and safe calls for repository tools (``repo-add`` and ``repo-remove``). Also this package contains ``ahriman.core.alpm.remote`` package which provides wrapper for remote sources (e.g. AUR RPC and official repositories RPC).
* ``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.auth`` package provides classes for authorization methods used by web mostly. Base class is ``ahriman.core.auth.Auth`` which must be instantiated by ``load`` method.
* ``ahriman.core.build_tools`` is a package which provides wrapper for ``devtools`` commands.
* ``ahriman.core.configuration`` contains extension for standard ``configparser`` library and some validation related classes.
* ``ahriman.core.database`` is everything including data and schema migrations for database.
* ``ahriman.core.database`` is everything for database, including data and schema migrations.
* ``ahriman.core.distributed`` package with triggers and helpers for distributed build system.
* ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers.
* ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly.
* ``ahriman.core.http`` package provides HTTP clients which can be later used by other classes.
* ``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.http`` package provides HTTP clients which can be used later by other classes.
* ``ahriman.core.log`` is a log utils package. It includes logger loader class, custom HTTP based logger and some wrappers.
* ``ahriman.core.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).
@ -94,7 +97,7 @@ All subcommands are divided into several groups depending on the role they are d
* ``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.
* ``patch`` subcommands (e.g. ``patch-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.
@ -105,7 +108,7 @@ For historical reasons and in order to keep backward compatibility some subcomma
Filesystem tree
---------------
The application supports two types of trees, one is for the legacy configuration (when there were no repository name explicit configuration available) and another one is the new-style tree. This document describes only new-style tree in order to avoid deprecated structures.
The application supports two types of trees, one is for the legacy configuration (when there were no explicit repository name configuration available) and another one is the new-style tree. This document describes only new-style tree in order to avoid deprecated structures.
Having default root as ``/var/lib/ahriman`` (differs from container though), the directory structure is the following:
@ -141,7 +144,7 @@ There are multiple subdirectories, some of them are commons for any repository,
* ``cache`` is a directory with locally stored PKGBUILD's and VCS packages. It is common for all repositories and architectures.
* ``chroot/{repository}`` is a chroot directory for ``devtools``. It is specific for each repository, but shared for different architectures inside (the ``devtools`` handles architectures automatically).
* ``packages/{repository}/{architecture}`` is a directory with prebuilt packages. When package is built, first it will be uploaded to this directory and later will be handled by update process. It is architecture and repository specific.
* ``packages/{repository}/{architecture}`` is a directory with prebuilt packages. When a package is built, first it will be uploaded to this directory and later will be handled by update process. It is architecture and repository specific.
* ``pacman/{repository}/{architecture}`` is the repository and architecture specific caches for pacman's databases.
* ``repository/{repository}/{architecture}`` is a repository packages directory.
@ -155,16 +158,16 @@ The service uses SQLite database in order to store some internal info.
Database instance
^^^^^^^^^^^^^^^^^
All methods related to specific part of database (basically operations per table) are split into different traits located inside ``ahriman.core.database.operations`` package. The base trait ``ahriman.core.database.operations.Operations`` also provides generic methods for database access (e.g. row converters and transactional support).
All methods related to the specific part of database (basically operations per table) are split into different traits located inside ``ahriman.core.database.operations`` package. The base trait ``ahriman.core.database.operations.Operations`` also provides generic methods for database access (e.g. row converters and transactional support).
The ``ahriman.core.database.SQLite`` class itself derives from all of these traits and implements methods for initialization, including migrations.
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.
The schema migrations are applied according to current ``pragma user_info`` values, located at ``ahriman.core.database.migrations`` package and named as ``m000_migration_name.py`` (the preceding ``m`` is required in order to import migration content for tests). Additional class ``ahriman.core.database.migrations.Migrations`` reads all migrations automatically and applies them in alphabetical order.
These migrations 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.
These migrations can also contain data migrations. Though the recommended way is to migrate data directly from SQL queries, 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
^^^^^^^^^^^^^^^^
@ -180,15 +183,16 @@ By default package build operations are performed with ``PACKAGER`` which is spe
* 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).
* If packager value has been found, it will be passed as ``PACKAGER`` system variable (additional sudo configuration might be required).
Add new packages or rebuild existing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Idea is to copy package to the directory from which it will be handled at the next update run. Different variants are supported:
Idea is to add package to a build queue from which it will be handled automatically during the next update run. Different variants are supported:
* If supplied argument is file then application moves the file to the directory with built packages. Same rule applies for directory, but in this case it copies every package-like file from the specified directory.
* If supplied argument is directory and there is ``PKGBUILD`` file there it will be treated as local package. In this case it will queue this package to build and copy source files (``PKGBUILD`` and ``.SRCINFO``) to caches.
* If supplied argument is file, then application moves the file to the directory with built packages. Same rule applies for directory, but in this case it copies every package-like file from the specified directory.
* If supplied argument is directory and there is ``PKGBUILD`` file there, it will be treated as local package. In this case it will queue this package to build and copy source files (``PKGBUILD`` and ``.SRCINFO``) to caches.
* If supplied argument looks like URL (i.e. it has scheme - e.g. ``http://`` which is neither ``data`` nor ``file``), it tries to download the package from the specified remote source.
* If supplied argument is not file then application tries to lookup for the specified name in AUR and clones it into the directory with manual updates. This scenario can also handle package dependencies which are missing in repositories.
This logic can be overwritten by specifying the ``source`` parameter, which is partially useful if you would like to add package from AUR, but there is local directory cloned from AUR. Also official repositories calls are hidden behind explicit source definition.
@ -203,10 +207,20 @@ Remove packages
This flow removes package from filesystem, updates repository database and also runs synchronization and reporting methods.
Check outdated packages
^^^^^^^^^^^^^^^^^^^^^^^
There are few ways for packages to be marked as out-of-date and hence requiring rebuild. Those are following:
#. User requested update of the package. It can be caused by calling ``package-add`` subcommand (or ``package-update`` with arguments).
#. The most common way for packages to be marked as out-of-dated is that the version in AUR (or the official repositories) is newer than in the repository.
#. In addition to the above, if package is named as VCS (e.g. has suffix ``-git``) and the last update was more than specified threshold ago, the service will also try to fetch sources and check if the revision is newer than the built one.
#. In addition, there is ability to check if the dependencies of the package have been updated (e.g. if linked library has been renamed or the modules directory - e.g. in case of python and ruby packages - has been changed). And if so, the package will be marked as out-of-dated as well.
Update packages
^^^^^^^^^^^^^^^
This feature is divided into to stages: check AUR for updates and run rebuild for required packages. Whereas check does not do anything except for check itself, update flow is the following:
This feature is divided into to the following stages: check AUR for updates and run rebuild for required packages. The package update flow is the following:
#. Process every built package first. Those packages are usually added manually.
#. Run sync and report methods.
@ -222,13 +236,33 @@ This feature is divided into to stages: check AUR for updates and run rebuild fo
After any step any package data is being removed.
In case if there are configured workers, the build process itself will be delegated to the remote instances. Packages will be partitioned to the chunks according to the amount of configured workers.
Distributed builds
^^^^^^^^^^^^^^^^^^
This feature consists of two parts:
* Upload built packages to the node.
* Delegate packages building to separated nodes.
The upload process is performed via special API endpoint, which is disabled by default, and is performed in several steps:
#. Upload package to temporary file.
#. Copy content from temporary file to the built package directory with dot (``.``) prefix.
#. Rename copied file, removing preceding dot.
After success upload, the update process must be called as usual in order to copy built packages to the main repository tree.
On the other side, the delegation uses upload feature, but in addition it also calls external services in order to trigger build process. The packages are separated to chunks based on the amount of the configured workers and their dependencies.
pkgrel bump rules
^^^^^^^^^^^^^^^^^
The application is able to automatically bump package release (``pkgrel``) during build process if there is duplicate version in repository. The version will be incremented as following:
#. Get version of the remote package.
#. Get version of the local package if any.
#. Get version of the local package if available.
#. If local version is not set, proceed with remote one.
#. If local version is set and epoch or package version (``pkgver``) are different, proceed with remote version.
#. If local version is set and remote version is newer than local one, proceed with remote.
@ -236,6 +270,24 @@ The application is able to automatically bump package release (``pkgrel``) durin
#. If it has ``major.minor`` notation (e.g. ``1.1``), then increment last part by 1, e.g. ``1.1 -> 1.2``, ``1.0.1 -> 1.0.2``.
#. If ``pkgrel`` is a number (e.g. ``1``), then append 1 to the end of the string, e.g. ``1 -> 1.1``.
Implicit dependencies resolution
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to the depends/optional/make/check depends lists the server also handles implicit dependencies. After success build, the application traverse through the build tree and finds
* Libraries to which the binaries (ELF-files) are linked. To do so, the ``NEEDED`` section of the ELF-files are read.
* Directories which contains files of the package, but do not belong to this package. This case covers, for example, python and ruby submodules.
Having the initial dependencies tree, the application is looking for packages which contains those (both files and directories) paths and creates the initial packages list. After that, the packages list is reduced in the following way:
* From any leaf exclude the package itself and possible debug packages.
* If the entry (i.e. file or directory) belongs to the package which is in base group, it will be removed.
* If there is a package which depends on the another package which provide the same entry, the package will be removed.
* After that, if there is a package which *optionally* depends on the another package in the remaining list, the package will be removed.
* And finally, if there is any path, which is the child of the entry, and it contains the same package, the package from the smaller entry will be removed.
All those implicit dependencies are stored in the database and extracted on each check. In case if any of the repository packages doesn't contain any entry anymore (e.g. so version has been changed or modules directory has been changed), the dependent package will be marked as out-of-dated.
Core functions reference
------------------------
@ -247,7 +299,7 @@ Configuration
Enumerations
^^^^^^^^^^^^
All enumerations are derived from ``str`` and ``enum.Enum``. Integer enumerations in general are not allowed, because most of operations require conversions from string variable. Derivation from string class is required to make json conversions implicitly (e.g. during calling ``json.dumps`` methods).
All enumerations are derived from ``enum.StrEnum``. Integer enumerations in general are not allowed, because most of operations require conversions from string variable. Derivation from string based enumeration is required to make json conversions implicitly (e.g. during calling ``json.dumps`` methods).
In addition, some enumerations provide ``from_option`` class methods in order to allow some flexibility while reading configuration options.
@ -319,9 +371,9 @@ There are several supported synchronization providers, currently they are ``rsyn
``rsync`` provider does not have any specific logic except for running external rsync application with configured arguments. The service does not handle SSH configuration, thus it has to be configured before running application manually.
``s3`` provider uses ``boto3`` package and implements sync feature. The files are stored in architecture directory (e.g. if bucket is ``repository``, packages will be stored in ``repository/aur-clone/x86_64`` for the ``aur-clone`` repository ``x86_64`` architecture), bucket must be created before any action and API key must have permissions to write to the bucket. No external configuration required. In order to upload only changed files the service compares calculated hashes with the Amazon ETags, used realization is described `here <https://teppen.io/2018/10/23/aws_s3_verify_etags/>`_.
``s3`` provider uses ``boto3`` package and implements sync feature. The files are stored in architecture specific directory (e.g. if bucket is ``repository``, packages will be stored in ``repository/aur-clone/x86_64`` for the ``aur-clone`` repository and ``x86_64`` architecture), bucket must be created before any action and API key must have permissions to write to the bucket. No external configuration required. In order to upload only changed files the service compares calculated hashes with the Amazon ETags, the implementation used is described `here <https://teppen.io/2018/10/23/aws_s3_verify_etags/>`__.
``github`` provider authenticates through basic auth, API key with repository write permissions is required. There will be created a release with the name of the architecture in case if it does not exist; files will be uploaded to the release assets. It also stores array of files and their MD5 checksums in release body in order to upload only changed ones. According to the Github API in case if there is already uploaded asset with the same name (e.g. database files), asset will be removed first.
``github`` provider authenticates through basic auth, API key with repository write permissions is required. There will be created a release with the name of the architecture in case if it does not exist; files will be uploaded to the release assets. It also stores array of files and their MD5 checksums in release body in order to upload only changed ones. According to the GitHub API in case if there is already uploaded asset with the same name (e.g. database files), asset will be removed first.
Additional features
^^^^^^^^^^^^^^^^^^^
@ -340,11 +392,10 @@ 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.
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation), ``aiohttp_cors`` (CORS support, required by documentation).
* 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.
* 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-unixsocket2`` library is required.
Middlewares
^^^^^^^^^^^
@ -363,7 +414,7 @@ Web views
All web views are defined in separated package and derived from ``ahriman.web.views.base.Base`` class which provides typed interfaces for web application.
REST API supports both form and JSON data, but the last one is recommended.
REST API supports only JSON data.
Different APIs are separated into different packages:
@ -377,12 +428,12 @@ The views are also divided by supporting API versions (e.g. ``v1``, ``v2``).
Templating
^^^^^^^^^^
Package provides base jinja templates which can be overridden by settings. Vanilla templates are actively using bootstrap library.
Package provides base jinja templates which can be overridden by settings. Vanilla templates actively use bootstrap library.
Requests and scopes
^^^^^^^^^^^^^^^^^^^
Service provides optional authorization which can be turned on in settings. In order to control user access there are two levels of authorization - read-only (only GET-like requests) and write (anything) which are provided by each web view directly.
Service provides optional authorization which can be turned on in settings. In order to control user access there are two levels of authorization - read-only (only GET-like requests) and write (anything), settings for which are provided by each web view directly.
If this feature is configured any request will be prohibited without authentication. In addition, configuration flag ``auth.allow_read_only`` can be used in order to allow read-only operations - reading index page and packages - without authorization.
@ -393,4 +444,4 @@ 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.
For most actions it also extracts user from authentication (if provided) and passes it to the underlying process.

View File

@ -39,11 +39,10 @@ It will check current settings on common errors and compare configuration with k
Base configuration settings.
* ``apply_migrations`` - perform migrations on application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations automatically.
* ``database`` - path to SQLite database, string, required.
* ``include`` - path to directory with configuration files overrides, string, required.
* ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
* ``database`` - path to the application SQLite database, string, required.
* ``include`` - path to directory with configuration files overrides, string, optional. Files will be read in alphabetical order.
* ``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:*`` groups
-----------------
@ -51,42 +50,49 @@ Base configuration settings.
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.
* ``repositories`` - list of pacman repositories, space separated list of strings, required.
* ``root`` - root for alpm library, string, required.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands).
* ``mirror`` - package database mirror used by pacman for synchronization, 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.
* ``repositories`` - list of pacman repositories, used for package search, space separated list of strings, required.
* ``root`` - root for alpm library, string, required. In the most cases it must point to the system root.
* ``sync_files_database`` - download files database from mirror, boolean, required.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands). If set to ``no``, databases must be synchronized manually.
``auth`` group
--------------
Base authorization settings. ``OAuth`` provider requires ``aioauth-client`` library to be installed.
* ``target`` - specifies authorization provider, string, optional, default ``disabled``. Allowed values are ``disabled``, ``configuration``, ``oauth``.
* ``target`` - specifies authorization provider, string, optional, default ``disabled``. Allowed values are ``disabled``, ``configuration``, ``oauth``, ``pam``.
* ``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_icon`` - OAuth2 login button icon, string, optional, default is ``google``. Must be valid `Bootstrap icon <https://icons.getbootstrap.com/>`_ name.
* ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 bytes URL-safe base64-encoded 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 invalidated during the service restart.
* ``full_access_group`` - name of the secondary group (e.g. ``wheel``) to be used as admin group in the service, string, required in case if ``pam`` is used.
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service in seconds, integer, optional, default is 7 days.
* ``oauth_icon`` - OAuth2 login button icon, string, optional, default is ``google``. Must be valid `Bootstrap icon <https://icons.getbootstrap.com/>`__ name.
* ``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.
* ``permit_root_login`` - allow login as root user, boolean, optional, default ``no``.
* ``salt`` - additional password hash salt, string, optional.
Authorized users are stored inside internal database, if any of external provides are used the password field for non-service users must be empty.
Authorized users are stored inside internal database, if any of external providers (e.g. ``oauth``) are used, the password field for non-service users must be empty.
``build:*`` groups
------------------
Build related configuration. Group name can refer to architecture, e.g. ``build:x86_64`` can be used for x86_64 architecture specific settings.
* ``allowed_scan_paths`` - paths to be used for implicit dependencies scan, scape separated list of paths, optional.
* ``archbuild_flags`` - additional flags passed to ``archbuild`` command, space separated list of strings, optional.
* ``blacklisted_scan_paths`` - paths to be excluded for implicit dependencies scan, scape separated list of paths, optional. Normally all elements of this option must be child paths of any of ``allowed_scan_paths`` element.
* ``build_command`` - default build command, string, required.
* ``ignore_packages`` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional.
* ``include_debug_packages`` - distribute debug packages, boolean, optional, default ``yes``.
* ``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`` - 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 definition.
* ``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``.
* ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, integer, optional, default is 7 days.
* ``workers`` - list of worker nodes addresses used for build process, space separated list of strings, optional. Each worker address must be valid and reachable URL, e.g. ``https://10.0.0.1:8080``. If none set, the build process will be run on the current node. There is also special trigger which loads this value based on the list of the discovered nodes.
``repository`` group
--------------------
@ -103,28 +109,35 @@ 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.
``web:*`` groups
``status`` group
----------------
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.
Reporting to web service related settings. In most cases there is fallback to web section settings.
* ``enabled`` - enable reporting to web service, boolean, optional, default ``yes`` for backward compatibility.
* ``address`` - remote web service address with protocol, string, optional. In case of websocket, the ``http+unix`` scheme and URL encoded address (e.g. ``%2Fvar%2Flib%2Fahriman`` for ``/var/lib/ahriman``) must be used, e.g. ``http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket``. In case if none set, it will be guessed from ``web`` section.
* ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.
* ``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.
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.
``web`` group
-------------
Web server 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.
* ``enable_archive_upload`` - allow to upload packages via HTTP (i.e. call of ``/api/v1/service/upload`` uri), boolean, optional, default ``no``.
* ``host`` - host to bind, string, optional.
* ``index_url`` - full url of the repository index page, string, optional.
* ``index_url`` - full URL of the repository index page, string, optional.
* ``max_body_size`` - max body size in bytes to be validated for archive upload, integer, optional. If not set, validation will be disabled.
* ``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.
* ``port`` - port to bind, integer, optional.
* ``service_only`` - disable status routes (including logs), boolean, optional, default ``no``.
* ``static_path`` - path to directory with static files, string, required.
* ``templates`` - path to templates directories, space separated list of strings, required.
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
* ``templates`` - path to templates directories, space separated list of paths, 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.
* ``wait_timeout`` - wait timeout in seconds, maximum amount of time to be waited before lock will be free, int, optional.
* ``wait_timeout`` - wait timeout in seconds, maximum amount of time to be waited before lock will be free, integer, optional.
``keyring`` group
--------------------
@ -138,10 +151,10 @@ Keyring generator plugin
* ``type`` - type of the generator, string, optional, must be set to ``keyring-generator`` if exists.
* ``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.
* ``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.
* ``packagers`` - list of packagers keys, space separated list of strings, optional, if not set, the user keys from database 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.
@ -157,7 +170,7 @@ Mirrorlist generator plugin
* ``type`` - type of the generator, string, optional, must be set to ``mirrorlist-generator`` if exists.
* ``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.
* ``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.
@ -168,10 +181,10 @@ Mirrorlist generator plugin
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.:
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>`_.
* by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`__.
Available options are:
@ -180,7 +193,7 @@ Available options are:
Remote pull trigger
^^^^^^^^^^^^^^^^^^^
* ``pull_url`` - url of the remote repository from which PKGBUILDs can be pulled before build process, string, required.
* ``pull_url`` - URL of the remote repository from which PKGBUILDs can be pulled before build process, string, required.
* ``pull_branch`` - branch of the remote repository from which PKGBUILDs can be pulled before build process, string, optional, default is ``master``.
``remote-push`` group
@ -188,10 +201,10 @@ 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. Alternatively, you can use any other option supported by git, e.g.:
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>`_.
* by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`__.
Available options are:
@ -202,7 +215,7 @@ Remote push trigger
* ``commit_email`` - git commit email, string, optional, default is ``ahriman@localhost``.
* ``commit_user`` - git commit user, string, optional, default is ``ahriman``.
* ``push_url`` - url of the remote repository to which PKGBUILDs should be pushed after build process, string, required.
* ``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``.
``report`` group
@ -237,13 +250,13 @@ Section name must be either ``email`` (plus optional architecture name, e.g. ``e
* ``link_path`` - prefix for HTML links, string, required.
* ``no_empty_report`` - skip report generation for empty packages list, boolean, optional, default ``yes``.
* ``password`` - SMTP password to authenticate, string, optional.
* ``port`` - SMTP port for sending emails, int, required.
* ``port`` - SMTP port for sending emails, integer, required.
* ``receivers`` - SMTP receiver addresses, space separated list of strings, required.
* ``sender`` - SMTP sender address, string, required.
* ``ssl`` - SSL mode for SMTP connection, one of ``ssl``, ``starttls``, ``disabled``, optional, default ``disabled``.
* ``template`` - Jinja2 template name, string, required.
* ``template_full`` - Jinja2 template name for full package description index, string, optional.
* ``templates`` - path to templates directories, space separated list of strings, required.
* ``templates`` - path to templates directories, space separated list of paths, required.
* ``user`` - SMTP user to authenticate, string, optional.
``html`` type
@ -256,7 +269,7 @@ Section name must be either ``html`` (plus optional architecture name, e.g. ``ht
* ``link_path`` - prefix for HTML links, string, required.
* ``path`` - path to html report file, string, required.
* ``template`` - Jinja2 template name, string, required.
* ``templates`` - path to templates directories, space separated list of strings, required.
* ``templates`` - path to templates directories, space separated list of paths, required.
``remote-call`` type
^^^^^^^^^^^^^^^^^^^^
@ -267,7 +280,7 @@ Section name must be either ``remote-call`` (plus optional architecture name, e.
* ``aur`` - check for AUR packages updates, boolean, optional, default ``no``.
* ``local`` - check for local packages updates, boolean, optional, default ``no``.
* ``manual`` - update manually built packages, boolean, optional, default ``no``.
* ``wait_timeout`` - maximum amount of time in seconds to be waited before remote process will be terminated, int, optional, default ``-1``.
* ``wait_timeout`` - maximum amount of time in seconds to be waited before remote process will be terminated, integer, optional, default ``-1``.
``telegram`` type
^^^^^^^^^^^^^^^^^
@ -281,8 +294,8 @@ Section name must be either ``telegram`` (plus optional architecture name, e.g.
* ``link_path`` - prefix for HTML links, string, required.
* ``template`` - Jinja2 template name, string, required.
* ``template_type`` - ``parse_mode`` to be passed to telegram API, one of ``MarkdownV2``, ``HTML``, ``Markdown``, string, optional, default ``HTML``.
* ``templates`` - path to templates directories, space separated list of strings, required.
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
* ``templates`` - path to templates directories, space separated list of paths, required.
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
``upload`` group
----------------
@ -306,13 +319,13 @@ This feature requires GitHub key creation (see below). Section name must be eith
* ``owner`` - GitHub repository owner, string, required.
* ``password`` - created GitHub API key. In order to create it do the following:
#. Go to `settings page <https://github.com/settings/profile>`_.
#. Switch to `developers settings <https://github.com/settings/apps>`_.
#. Switch to `personal access tokens <https://github.com/settings/tokens>`_.
#. Go to `settings page <https://github.com/settings/profile>`__.
#. Switch to `developers settings <https://github.com/settings/apps>`__.
#. Switch to `personal access tokens <https://github.com/settings/tokens>`__.
#. Generate new token. Required scope is ``public_repo`` (or ``repo`` for private repository support).
* ``repository`` - GitHub repository name, string, required. Repository must be created before any action and must have active branch (e.g. with readme).
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
* ``use_full_release_name`` - if set to ``yes``, the release will contain both repository name and architecture, and only architecture otherwise, boolean, optional, default ``no`` (legacy behavior).
* ``username`` - GitHub authorization user, string, required. Basically the same as ``owner``.
@ -322,7 +335,7 @@ This feature requires GitHub key creation (see below). Section name must be eith
Section name must be either ``remote-service`` (plus optional architecture name, e.g. ``remote-service:x86_64``) or random name with ``type`` set.
* ``type`` - type of the report, string, optional, must be set to ``remote-service`` if exists.
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
``rsync`` type
^^^^^^^^^^^^^^
@ -341,7 +354,16 @@ Requires ``boto3`` library to be installed. Section name must be either ``s3`` (
* ``type`` - type of the upload, string, optional, must be set to ``s3`` if exists.
* ``access_key`` - AWS access key ID, string, required.
* ``bucket`` - bucket name (e.g. ``bucket``), string, required.
* ``chunk_size`` - chunk size for calculating entity tags, int, optional, default 8 * 1024 * 1024.
* ``chunk_size`` - chunk size for calculating entity tags, integer, optional, default 8 * 1024 * 1024.
* ``object_path`` - path prefix for stored objects, string, optional. If none set, the prefix as in repository tree will be used.
* ``region`` - bucket region (e.g. ``eu-central-1``), string, required.
* ``secret_key`` - AWS secret access key, string, required.
* ``secret_key`` - AWS secret access key, string, required.
``worker`` group
----------------
This section controls settings for ``ahriman.core.distributed.WorkerTrigger`` plugin.
* ``address`` - address of the instance, string, required. Must be reachable for the master instance.
* ``identifier`` - unique identifier of the instance, string, optional.
* ``time_to_live`` - amount of time which remote worker will be considered alive in seconds, integer, optional, default is ``60``. The ping interval will be set automatically equal this value divided by 4.

File diff suppressed because it is too large Load Diff

35
docs/faq/backup.rst Normal file
View File

@ -0,0 +1,35 @@
Backup and restore
------------------
The service provides several commands aim to do easy repository backup and restore. If you would like to move repository from the server ``server1.example.com`` to another ``server2.example.com`` you have to perform the following steps:
#.
On the source server ``server1.example.com`` run ``repo-backup`` command, e.g.:
.. code-block:: shell
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.
#.
Copy created archive from source server ``server1.example.com`` to target ``server2.example.com``.
#.
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
ahriman repo-restore /tmp/repo.tar.gz
An additional argument ``-o``/``--output`` can be used to specify extraction root (``/`` by default).
#.
Rebuild repository:
.. code-block:: shell
sudo -u ahriman ahriman repo-rebuild --from-database

320
docs/faq/distributed.rst Normal file
View File

@ -0,0 +1,320 @@
Distributed builds
------------------
The service allows to run build on multiple machines and collect packages on main node. There are several ways to achieve it, this section describes officially supported methods.
Remote synchronization and remote server call
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This setup requires at least two instances of the service:
#. Web service (with opt-in authorization enabled), later will be referenced as ``master`` node.
#. Application instances responsible for build, later will be referenced as ``worker`` nodes.
In this example the following settings are assumed:
* Repository architecture is ``x86_64``.
* Master node address is ``master.example.com``.
Master node configuration
"""""""""""""""""""""""""
The only requirements for the master node is that API must be available for worker nodes to call (e.g. port must be exposed to internet, or local network in case of VPN, etc) and file upload must be enabled:
.. code-block:: ini
[web]
enable_archive_upload = yes
In addition, the following settings are recommended for the master node:
*
As it has been mentioned above, it is recommended to enable authentication (see :doc:`How to enable basic authorization <web>`) and create system user which will be used later. Later this user (if any) will be referenced as ``worker-user``.
*
In order to be able to spawn multiple processes at the same time, wait timeout must be configured:
.. code-block:: ini
[web]
wait_timeout = 0
Worker nodes configuration
""""""""""""""""""""""""""
#.
First of all, in this setup you need to split your repository into chunks manually, e.g. if you have repository on master node with packages ``A``, ``B`` and ``C``, you need to split them between all available workers, as example:
* Worker #1: ``A``.
* Worker #2: ``B`` and ``C``.
Hint: ``repo-tree`` subcommand provides ``--partitions`` argument.
#.
Each worker must be configured to upload files to master node:
.. code-block:: ini
[upload]
target = remote-service
[remote-service]
#.
Worker must be configured to access web on master node:
.. code-block:: ini
[status]
address = https://master.example.com
username = worker-user
password = very-secure-password
As it has been mentioned above, ``status.address`` must be available for workers. In case if unix socket is used, it can be passed in the same option as usual. Optional ``status.username``/``status.password`` can be supplied in case if authentication was enabled on master node.
#.
Each worker must call master node on success:
.. code-block:: ini
[report]
target = remote-call
[remote-call]
manual = yes
After success synchronization (see above), the built packages will be put into directory, from which they will be read during manual update, thus ``remote-call.manual`` flag is required.
#.
Change order of trigger runs. This step is required, because by default the report trigger is called before the upload trigger and we would like to achieve the opposite:
.. code-block:: ini
[build]
triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.upload.UploadTrigger ahriman.core.report.ReportTrigger ahriman.core.gitremote.RemotePushTrigger
In addition, the following settings are recommended for workers:
*
You might want to wait until report trigger will be completed; in this case the following option must be set:
.. code-block:: ini
[remote-call]
wait_timeout = 0
Dependency management
"""""""""""""""""""""
By default worker nodes don't know anything about master nodes packages, thus it will try to build each dependency by its own. However, using ``AHRIMAN_REPOSITORY_SERVER`` docker variable (or ``--server`` flag for setup command), it is possible to specify address of the master node for devtools configuration.
Repository and packages signing
"""""""""""""""""""""""""""""""
You can sign packages on worker nodes and then signatures will be synced to master node. In order to do so, you need to configure worker node as following, e.g.:
.. code-block:: ini
[sign]
target = package
key = 8BE91E5A773FB48AC05CC1EDBED105AED6246B39
Note, however, that in this case, signatures will not be validated on master node and just will be copied to repository tree.
If you would like to sign only database files (aka repository sign), it has to be configured only on master node as usual, e.g.:
.. code-block:: ini
[sign]
target = repository
key = 8BE91E5A773FB48AC05CC1EDBED105AED6246B39
Double node minimal docker example
""""""""""""""""""""""""""""""""""
Master node config (``master.ini``) as:
.. code-block:: ini
[auth]
target = configuration
[web]
enable_archive_upload = yes
wait_timeout = 0
Command to run master node:
.. code-block:: shell
docker run --privileged -p 8080:8080 -e AHRIMAN_PORT=8080 -v master.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
The user ``worker-user`` has been created additionally. Worker node config (``worker.ini``) as:
.. code-block:: ini
[status]
address = http://172.17.0.1:8080
username = worker-user
password = very-secure-password
[upload]
target = remote-service
[remote-service]
[report]
target = remote-call
[remote-call]
manual = yes
wait_timeout = 0
[build]
triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.upload.UploadTrigger ahriman.core.report.ReportTrigger ahriman.core.gitremote.RemotePushTrigger
The address above (``http://172.17.0.1:8080``) is somewhat available for worker container.
Command to run worker node:
.. code-block:: shell
docker run --privileged -v worker.ini:/etc/ahriman.ini.d/overrides.ini -it arcan1s/ahriman:latest package-add ahriman --now
The command above will successfully build ``ahriman`` package, upload it on master node and, finally, will update master node repository.
Check proof-of-concept setup `here <https://github.com/arcan1s/ahriman/tree/master/recipes/distributed-manual>`__.
Addition of new package and repository update
"""""""""""""""""""""""""""""""""""""""""""""
Just run on worker command as usual, the built packages will be automatically uploaded to master node. Note that automatic update process must be disabled on master node.
Package removal
"""""""""""""""
This action must be done in two steps:
#. Remove package on worker.
#. Remove package on master node.
Delegate builds to remote workers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This setup heavily uses upload feature described above and, in addition, also delegates build process automatically to build machines. Same as above, there must be at least two instances available (``master`` and ``worker``), however, all ``worker`` nodes must be run in the web service mode.
Master node configuration
"""""""""""""""""""""""""
In addition to the configuration above, the worker list must be defined in configuration file (``build.workers`` option), i.e.:
.. code-block:: ini
[build]
workers = https://worker1.example.com https://worker2.example.com
[web]
enable_archive_upload = yes
wait_timeout = 0
In the example above, ``https://worker1.example.com`` and ``https://worker2.example.com`` are remote ``worker`` node addresses available for ``master`` node.
In case if authentication is required (which is recommended way to setup it), it can be set by using ``status`` section as usual.
Worker nodes configuration
""""""""""""""""""""""""""
It is required to point to the master node repository, otherwise internal dependencies will not be handled correctly. In order to do so, the ``--server`` argument (or ``AHRIMAN_REPOSITORY_SERVER`` environment variable for docker images) can be used.
Also, in case if authentication is enabled, the same user with the same password must be created for all workers.
It is also recommended to set ``web.wait_timeout`` to infinite in case of multiple conflicting runs and ``service_only`` to ``yes`` in order to disable status endpoints.
Other settings are the same as mentioned above.
Triple node minimal docker example
""""""""""""""""""""""""""""""""""
In this example, all instances are run on the same machine with address ``172.17.0.1`` with ports available outside of container. Master node config (``master.ini``) as:
.. code-block:: ini
[auth]
target = configuration
[status]
username = builder-user
password = very-secure-password
[build]
workers = http://172.17.0.1:8081 http://172.17.0.1:8082
[web]
enable_archive_upload = yes
wait_timeout = 0
Command to run master node:
.. code-block:: shell
docker run --privileged -p 8080:8080 -e AHRIMAN_PORT=8080 -v master.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
Worker nodes (applicable for all workers) config (``worker.ini``) as:
.. code-block:: ini
[auth]
target = configuration
[status]
address = http://172.17.0.1:8080
username = builder-user
password = very-secure-password
[upload]
target = remote-service
[remote-service]
[report]
target = remote-call
[remote-call]
manual = yes
wait_timeout = 0
[web]
service_only = yes
[build]
triggers = ahriman.core.upload.UploadTrigger ahriman.core.report.ReportTrigger
Command to run worker nodes (considering there will be two workers, one is on ``8081`` port and other is on ``8082``):
.. code-block:: ini
docker run --privileged -p 8081:8081 -e AHRIMAN_PORT=8081 -v worker.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
docker run --privileged -p 8082:8082 -e AHRIMAN_PORT=8082 -v worker.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
Unlike the previous setup, it doesn't require to mount repository root for ``worker`` nodes, because they don't use it anyway.
Check proof-of-concept setup `here <https://github.com/arcan1s/ahriman/tree/master/recipes/distributed>`__.
Addition of new package, package removal, repository update
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
In all scenarios, update process must be run only on ``master`` node. Unlike the manually distributed packages described above, automatic update must be enabled only for ``master`` node.
Automatic worker nodes discovery
""""""""""""""""""""""""""""""""
Instead of setting ``build.workers`` option it is also possible to configure services to load worker list dynamically. To do so, the ``ahriman.core.distributed.WorkerLoaderTrigger`` and ``ahriman.core.distributed.WorkerTrigger`` must be used for ``master`` and ``worker`` nodes repsectively. See recipes for more details.
Known limitations
"""""""""""""""""
* Workers don't support local packages. However, it is possible to build custom packages by providing sources by using ``ahriman.core.gitremote.RemotePullTrigger`` trigger.
* No dynamic nodes discovery. In case if one of worker nodes is unavailable, the build process will fail.
* No pkgrel bump on conflicts.
* The identical user must be created for all workers. However, the ``master`` node user can be different from this one.

115
docs/faq/docker.rst Normal file
View File

@ -0,0 +1,115 @@
Docker image
------------
We provide official images which can be found under:
* docker registry ``arcan1s/ahriman``;
* ghcr.io registry ``ghcr.io/arcan1s/ahriman``.
These images are totally identical.
Docker image is being updated on each commit to master as well as on each version. If you would like to use last (probably unstable) build you can use ``edge`` tag or ``latest`` for any tagged versions; otherwise you can use any version tag available.
The default action (in case if no arguments provided) is ``repo-update``. Basically the idea is to run container, e.g.:
.. code-block:: shell
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 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 --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 --privileged -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest package-add ahriman --now
For more details please refer to the docker FAQ.
Environment variables
^^^^^^^^^^^^^^^^^^^^^
The following environment variables are supported:
* ``AHRIMAN_ARCHITECTURE`` - architecture of the repository, default is ``x86_64``.
* ``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_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_POSTSETUP_COMMAND`` - if set, the command which will be called (as root) after the setup command, but before any other actions.
* ``AHRIMAN_PRESETUP_COMMAND`` - if set, the command which will be called (as root) right before the setup command.
* ``AHRIMAN_REPOSITORY`` - repository name, default is ``aur-clone``.
* ``AHRIMAN_REPOSITORY_SERVER`` - optional override for the repository URL. Useful if you would like to download packages from remote instead of local filesystem.
* ``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_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 ``/run/ahriman``.
* ``AHRIMAN_USER`` - ahriman user, usually must not be overwritten, default is ``ahriman``.
* ``AHRIMAN_VALIDATE_CONFIGURATION`` - if set (default) validate service configuration.
You can pass any of these variables by using ``-e`` argument, e.g.:
.. code-block:: shell
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. Check also `examples <https://github.com/arcan1s/ahriman/tree/master/recipes/daemon>`__.
Web service setup
^^^^^^^^^^^^^^^^^
For that you would need to have web container instance running forever; it can be achieved by the following command:
.. code-block:: shell
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.
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_UNIX_SOCKET=/var/lib/ahriman/ahriman/ahriman-web.sock -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
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 --privileged --net=host -e AHRIMAN_PORT=8080 -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Simple server with authentication can be found in `examples <https://github.com/arcan1s/ahriman/tree/master/recipes/web>`__ too.
Mutli-repository web service
""""""""""""""""""""""""""""
Idea is pretty same as to just run web service. However, it is required to run setup commands for each repository, except for one which is specified by ``AHRIMAN_REPOSITORY`` and ``AHRIMAN_ARCHITECTURE`` variables.
In order to create configuration for additional repositories, the ``AHRIMAN_POSTSETUP_COMMAND`` variable should be used, e.g.:
.. code-block:: shell
docker run --privileged -p 8080:8080 -e AHRIMAN_PORT=8080 -e AHRIMAN_UNIX_SOCKET=/var/lib/ahriman/ahriman/ahriman-web.sock -e AHRIMAN_POSTSETUP_COMMAND="ahriman --architecture x86_64 --repository aur-clone-v2 service-setup --build-as-user ahriman --packager 'ahriman bot <ahriman@example.com>'" -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
The command above will also create configuration for the repository named ``aur-clone-v2``.
Note, however, that the command above is only required in case if the service is going to be used to run subprocesses. Otherwise, everything else (web interface, status, etc) will be handled as usual.
Configuration `example <https://github.com/arcan1s/ahriman/tree/master/recipes/multirepo>`__.

12
docs/faq/examples.rst Normal file
View File

@ -0,0 +1,12 @@
Use cases
---------
There is a collection of some specific recipes which can be found in `the repository <https://github.com/arcan1s/ahriman/tree/master/recipes>`__.
Most of them can be run (``AHRIMAN_PASSWORD`` environment variable is required in the most setups) as simple as:
.. code-block:: shell
AHRIMAN_PASSWORD=demo docker compose up
Note, however, they are just an examples of specific configuration for specific cases and they are never intended to be used as is in real environment.

431
docs/faq/general.rst Normal file
View File

@ -0,0 +1,431 @@
General topics
--------------
What is the purpose of the project
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This project has been created in order to maintain self-hosted Arch Linux user repository without manual intervention - checking for updates and building packages.
How to install ahriman
^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
yay -S ahriman
ahriman -a x86_64 -r aur-clone service-setup --packager "ahriman bot <ahriman@example.com>"
systemctl enable --now ahriman@x86_64-aur-clone.timer
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>`.
Run as daemon
"""""""""""""
The alternative way (though not recommended) is to run service instead of timer:
.. code-block:: shell
systemctl enable --now ahriman-daemon@x86_64-aur-clone
How to validate settings
^^^^^^^^^^^^^^^^^^^^^^^^
There is special command which can be used in order to validate current configuration:
.. code-block:: shell
ahriman 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some sections can be configured per architecture. The service will merge architecture specific values into common settings. In order to specify settings for specific architecture you must point it in section name.
For example, the section
.. code-block:: ini
[build]
build_command = extra-x86_64-build
states that default build command is ``extra-x86_64-build``. But if there is section
.. code-block:: ini
[build:i686]
build_command = extra-i686-build
the ``extra-i686-build`` command will be used for ``i686`` architecture. You can also override settings for different repositories and architectures; in this case section names will be ``build:aur-clone`` (repository name only) and ``build:aur-clone:i686`` (both repository name and architecture).
How to generate build reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally you would probably like to generate only one report for the specific type, e.g. only one email report. In order to do so you will need to have the following configuration:
.. code-block:: ini
[report]
target = email
[email]
...
or in case of multiple architectures and *different* reporting settings:
.. code-block:: ini
[report]
target = email
[email:i686]
...
[email:x86_64]
...
But for some cases you would like to have multiple different reports with the same type (e.g. sending different templates to different addresses). For these cases you will need to specify section name in target and type in section, e.g. the following configuration can be used:
.. code-block:: ini
[report]
target = email_1 email_2
[email_1]
type = email
...
[email_2]
type = email
...
How to add new package
^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
sudo -u ahriman ahriman package-add ahriman --now
``--now`` flag is totally optional and just run ``repo-update`` subcommand after the registering the new package. Thus the extended flow is the following:
.. code-block:: shell
sudo -u ahriman ahriman package-add ahriman
sudo -u ahriman ahriman repo-update
How to build package from local PKGBUILD
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
sudo -u ahriman ahriman package-add /path/to/local/directory/with/PKGBUILD --now
Before using this command you will need to create local directory, put ``PKGBUILD`` there and generate ``.SRCINFO`` by using ``makepkg --printsrcinfo > .SRCINFO`` command. These packages will be stored locally and *will be ignored* during automatic update; in order to update the package you will need to run ``package-add`` command again.
How to copy package from another repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As simple as add package from archive. Considering case when you would like to copy package ``package`` with version ``ver-rel`` from repository ``source-repository`` to ``target-respository`` (same architecture), the command will be following:
.. code-block:: shell
sudo -u ahriman ahriman -r target-repository package-add /var/lib/ahriman/repository/source-repository/x86_64/package-ver-rel-x86_64.pkg.tar.zst
In addition, you can remove source package as usual later.
This feature in particular useful if for managing multiple repositories like ``[testing]`` and ``[extra]``.
How to fetch PKGBUILDs from remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will need to configure trigger as following:
.. code-block:: ini
[remote-pull]
target = gitremote
[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.
This feature can be also used to build packages which are not listed in AUR, the example of the feature use can be found `here <https://github.com/arcan1s/ahriman/tree/master/recipes/pull>`__.
How to push updated PKGBUILDs to remote repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For that purpose you'd need to use another trigger called ``RemotePushTrigger``. Configure trigger as following:
.. code-block:: ini
[remote-push]
target = gitremote
[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
.. 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
The command also supports arrays, but in this case you need to specify full array, e.g.
.. code-block:: shell
sudo -u ahriman ahriman patch-add ahriman depends
Post new function or variable value below. Press Ctrl-D to finish:
(python python-aiohttp)
^D
will set depends PKGBUILD variable (exactly) to array ``["python", "python-aiohttp"]``.
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 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).
It is also possible to create simple patch during package addition, e.g.:
.. code-block:: shell
sudo -u ahriman ahriman package-add ahriman --variable PKGEXT=.pkg.tar.xz
The ``--variable`` argument accepts variables in shell like format: quotation and lists are supported as usual, but functions are not. This feature is useful in particular in order to override specific makepkg variables during build.
How to build package from official repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is the same as adding any other package, but due to restrictions you must specify source explicitly, e.g.:
.. code-block:: shell
sudo -u ahriman ahriman package-add pacman --source repository
This feature is heavily depends on local pacman cache. In order to use this feature it is recommended to either run ``pacman -Sy`` before the interaction or use internal application cache with ``--refresh`` flag.
Package build fails because it cannot validate PGP signature of source files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
sudo -u ahriman ahriman service-key-import ...
How to update VCS packages
^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally the service handles VCS packages correctly, however it requires additional dependencies:
.. code-block:: shell
pacman -S breezy darcs mercurial subversion
How to review changes before build
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this scenario, the update process must be separated into several stages. First, it is required to check updates:
.. code-block:: shell
sudo -u ahriman ahriman repo-check
During the check process, the service will generate changes from the last known commit and will send it to remote service. In order to verify source files changes, the web interface or special subcommand can be used:
.. code-block:: shell
ahriman package-changes ahriman
After validation, the operator can run update process with approved list of packages, e.g.:
.. code-block:: shell
sudo -u ahriman ahriman repo-update ahriman
How to remove package
^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
sudo -u ahriman ahriman package-remove ahriman
Also, there is command ``repo-remove-unknown`` which checks packages in AUR and local storage and removes ones which have been removed.
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 different. 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 fingerprint, something like ``8BE91E5A773FB48AC05CC1EDBED105AED6246B39``. Copy it.
#.
Export your private key by using the fingerprint 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
sudo -u ahriman ahriman repo-rebuild --depends-on python
You can even rebuild the whole repository (which is particular useful in case if you would like to change packager) if you do not supply ``--depends-on`` option. This action will automatically increment ``pkgrel`` value; in case if you don't want to, the ``--no-increment`` option has to be supplied.
However, note that you do not need to rebuild repository in case if you just changed signing option, just use ``repo-sign`` command instead.
Automated broken dependencies detection
"""""""""""""""""""""""""""""""""""""""
After the success build the application extracts all linked libraries and used directories and stores them in database. During the check process, the application extracts pacman databases and checks if file names have been changed (e.g. new python release caused ``/usr/lib/python3.x`` directory renaming to ``/usr/lib/python3.y`` or soname for a linked library has been changed). In case if broken dependencies have been detected, the package will be added to the rebuild queue.
In order to disable this check completely, the ``--no-check-files`` flag can be used.
In addition, there is possibility to control paths which will be used for checking, by using options ``build.allowed_scan_paths`` and ``build.blacklisted_scan_paths``. Leaving ``build.allowed_scan_paths`` blank will effectively disable any check too.
How to install built packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to your ``pacman.conf``:
.. code-block:: ini
[repository]
Server = file:///var/lib/ahriman/repository/$repo/$arch
(You might need to add ``SigLevel`` option according to the pacman documentation.)
How to serve repository
^^^^^^^^^^^^^^^^^^^^^^^
Easy. For example, nginx configuration (without SSL) will look like:
.. code-block::
server {
listen 80;
server_name repo.example.com;
location / {
autoindex on;
root /var/lib/ahriman/repository;
}
}
Example of the status page configuration is the following (status service is using 8080 port):
.. code-block::
server {
listen 80;
server_name builds.example.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
proxy_pass http://127.0.0.1:8080;
}
}
Some more examples can be found in configuration `recipes <https://github.com/arcan1s/ahriman/tree/master/recipes>`__.

17
docs/faq/index.rst Normal file
View File

@ -0,0 +1,17 @@
FAQ
===
.. toctree::
:maxdepth: 2
general
docker
non-x86_64-setup
synchronization
reporting
distributed
maintenance-packages
web
backup
examples
misc

View File

@ -0,0 +1,73 @@
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.

100
docs/faq/misc.rst Normal file
View File

@ -0,0 +1,100 @@
Other topics
------------
How does it differ from %another-manager%?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Short answer - I do not know. Also for some references credits to `Alad <https://github.com/AladW>`__, he `did <https://wiki.archlinux.org/title/User:Alad/Local_repo_tools>`__ really good investigation of existing alternatives.
`arch-repo-manager <https://github.com/Martchus/arch-repo-manager>`__
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Looks actually pretty good, in case if I would find it, I would probably didn't start this project; the most of features (like web interface or additional helpers) are already implemented or planned to be. However, this project seems to be at early alpha stage (as for Nov 2022), written in C++ (not pro or con) and misses documentation.
`archrepo2 <https://github.com/lilydjwg/archrepo2>`__
"""""""""""""""""""""""""""""""""""""""""""""""""""""
Don't know, haven't tried it. But it lacks of documentation at least.
* ``ahriman`` has web interface.
* ``archrepo2`` doesn't have synchronization and reporting.
* ``archrepo2`` actively uses direct shell calls and ``yaourt`` components.
* ``archrepo2`` has constantly running process instead of timer process (it is not pro or con).
`repoctl <https://github.com/cassava/repoctl>`__
""""""""""""""""""""""""""""""""""""""""""""""""
* ``ahriman`` has web interface.
* ``repoctl`` does not have reporting feature.
* ``repoctl`` does not support local packages and patches.
* Some actions are not fully automated in ``repoctl`` (e.g. package update still requires manual intervention for the build itself).
* ``repoctl`` has better AUR interaction features. With colors!
* ``repoctl`` has much easier configuration and even completion.
* ``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>`__
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Though originally I've created ahriman by trying to improve the project, it still lacks a lot of features:
* ``ahriman`` has web interface.
* ``ahriman`` has better reporting with template support.
* ``ahriman`` has more synchronization features (there was only ``rsync`` based).
* ``ahriman`` supports local packages and patches.
* ``repo-scripts`` doesn't have dependency management.
...and so on. ``repo-scripts`` also has bad architecture and bad quality code and uses out-of-dated ``yaourt`` and ``package-query``.
`toolbox <https://github.com/chaotic-aur/toolbox>`__
""""""""""""""""""""""""""""""""""""""""""""""""""""
It is automation tools for ``repoctl`` mentioned above. Except for using shell it looks pretty cool and also offers some additional features like patches, remote synchronization (isn't it?) and reporting.
How to check service logs
^^^^^^^^^^^^^^^^^^^^^^^^^
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
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).
Html customization
^^^^^^^^^^^^^^^^^^
It is possible to customize html templates. In order to do so, create files somewhere (refer to Jinja2 documentation and the service source code for available parameters) and prepend ``templates`` with value pointing to this directory.
In addition, default html templates supports style customization out-of-box. In order to customize style, just put file named ``user-style.jinja2`` to the templates directory.
Web API extension
^^^^^^^^^^^^^^^^^
The application loads web views dynamically, so it is possible relatively easy extend its API. In order to do so:
#. Create view class which is derived from ``ahriman.web.views.base.BaseView`` class.
#. Create implementation for this class.
#. Put file into ``ahriman.web.views`` package.
#. Restart application.
For more details about implementation and possibilities, kindly refer to module documentation and source code and `aiohttp documentation <https://docs.aiohttp.org/en/stable/>`__.
I did not find my question
^^^^^^^^^^^^^^^^^^^^^^^^^^
`Create an issue <https://github.com/arcan1s/ahriman/issues>`__ with type **Question**.

View File

@ -0,0 +1,99 @@
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.
The example of setup with docker compose can be found `here <https://github.com/arcan1s/ahriman/tree/master/recipes/i686>`__.
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, e.g.:
.. code-block:: shell
wget https://pool.mirror.archlinux32.org/i686/core/archlinux32-keyring-20230705-1.0-any.pkg.tar.zst
pacman -U archlinux32-keyring-20230705-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, e.g.:
.. code-block:: shell
wget https://pool.mirror.archlinux32.org/i686/extra/devtools-20221208-1.2-any.pkg.tar.zst
pacman -U devtools-20221208-1.2-any.pkg.tar.zst
Alternatively, you can create your own ``makepkg`` configuration and save it as ``/usr/share/devtools/makepkg.conf.d/i686.conf``.
#.
Setup repository as usual:
.. code-block:: shell
ahriman -a i686 service-setup --mirror 'https://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.
#.
That's all Folks!
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 https://pool.mirror.archlinux32.org/i686/extra/devtools-20221208-1.2-any.pkg.tar.zst && pacman --noconfirm -U devtools-20221208-1.2-any.pkg.tar.zst
RUN wget https://pool.mirror.archlinux32.org/i686/core/archlinux32-keyring-20230705-1.0-any.pkg.tar.zst && pacman --noconfirm -U archlinux32-keyring-20230705-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 https://pool.mirror.archlinux32.org/i686/extra/devtools-20221208-1.2-any.pkg.tar.zst && pacman --noconfirm -U devtools-20221208-1.2-any.pkg.tar.zst
RUN wget https://pool.mirror.archlinux32.org/i686/core/archlinux32-keyring-20230705-1.0-any.pkg.tar.zst && pacman --noconfirm -U archlinux32-keyring-20230705-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='https://de.mirror.archlinux32.org/$arch/$repo' -e AHRIMAN_MULTILIB= ahriman-i686:latest

99
docs/faq/reporting.rst Normal file
View File

@ -0,0 +1,99 @@
Reporting
---------
How to report by email
^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
yay -S --asdeps python-jinja
#.
Configure the service:
.. code-block:: ini
[report]
target = email
[email]
host = smtp.example.com
link_path = http://example.com/aur-clone/x86_64
password = ...
port = 465
receivers = me@example.com
sender = me@example.com
user = me@example.com
How to generate index page for S3
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
yay -S --asdeps python-jinja
#.
Configure the service:
.. code-block:: ini
[report]
target = html
[html]
path = /var/lib/ahriman/repository/aur-clone/x86_64/index.html
link_path = http://example.com/aur-clone/x86_64
After these steps ``index.html`` file will be automatically synced to S3.
How to post build report to telegram
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
It still requires additional dependencies:
.. code-block:: shell
yay -S --asdeps python-jinja
#.
Register bot in telegram. You can do it by starting chat with `@BotFather <https://t.me/botfather>`__. For more details please refer to `official documentation <https://core.telegram.org/bots>`__.
#.
Optionally (if you want to post message in chat):
#. Create telegram channel.
#. Invite your bot into the channel.
#. Make your channel public
#.
Get chat id if you want to use by numerical id or just use id prefixed with ``@`` (e.g. ``@ahriman``). If you are not using chat the chat id is your user id. If you don't want to make channel public you can use `this guide <https://stackoverflow.com/a/33862907>`__.
#.
Configure the service:
.. code-block:: ini
[report]
target = telegram
[telegram]
api_key = aaAAbbBBccCC
chat_id = @ahriman
link_path = http://example.com/aur-clone/x86_64
``api_key`` is the one sent by `@BotFather <https://t.me/botfather>`__, ``chat_id`` is the value retrieved from previous step.
If you did everything fine you should receive the message with the next update. Quick credentials check can be done by using the following command:
.. code-block:: shell
curl 'https://api.telegram.org/bot{api_key}/sendMessage?chat_id={chat_id}&text=hello'
(replace ``{chat_id}`` and ``{api_key}`` with the values from configuration).

View File

@ -0,0 +1,131 @@
Remote synchronization
----------------------
How to sync repository to another server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are several choices:
#.
Easy and cheap, just share your local files through the internet, e.g. for ``nginx``:
.. code-block::
server {
location / {
autoindex on;
root /var/lib/ahriman/repository/;
}
}
#.
You can also upload your packages using ``rsync`` to any available server. In order to use it you would need to configure ahriman first:
.. code-block:: ini
[upload]
target = rsync
[rsync]
remote = 192.168.0.1:/srv/repo
After that just add ``/srv/repo`` to the ``pacman.conf`` as usual. You can also upload to S3 (``Server = https://s3.eu-central-1.amazonaws.com/repository/aur-clone/x86_64``) or to GitHub (``Server = https://github.com/ahriman/repository/releases/download/aur-clone-x86_64``).
How to sync to S3
^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
pacman -S python-boto3
#.
Create a bucket (e.g. ``repository``).
#.
Create an user with write access to the bucket:
.. code-block::
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListObjectsInBucket",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::repository"
]
},
{
"Sid": "AllObjectActions",
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": [
"arn:aws:s3:::repository/*"
]
}
]
}
#.
Create an API key for the user and store it.
#.
Configure the service as following:
.. code-block:: ini
[upload]
target = s3
[s3]
access_key = ...
bucket = repository
region = eu-central-1
secret_key = ...
S3 with SSL
"""""""""""
In order to configure S3 on custom domain with SSL (and some other features, like redirects), the CloudFront should be used.
#. Configure S3 as described above.
#. In bucket properties, enable static website hosting with hosting type "Host a static website".
#. Go to AWS Certificate Manager and create public certificate on your domain. Validate domain as suggested.
#. Go to CloudFront and create distribution. The following settings are required:
* Origin domain choose S3 bucket.
* Tick use website endpoint.
* Disable caching.
* Select issued certificate.
#. Point DNS record to CloudFront address.
How to sync to GitHub releases
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create a repository.
#.
`Create API key <https://github.com/settings/tokens>`__ with scope ``public_repo``.
#.
Configure the service as following:
.. code-block:: ini
[upload]
target = github
[github]
owner = ahriman
password = ...
repository = repository
username = ahriman

145
docs/faq/web.rst Normal file
View File

@ -0,0 +1,145 @@
Web service
-----------
How to setup web service
^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
yay -S --asdeps python-aiohttp python-aiohttp-jinja2 python-aiohttp-apispec>=3.0.0 python-aiohttp-cors
#.
Configure service:
.. code-block:: ini
[web]
port = 8080
#.
Start the web service ``systemctl enable --now ahriman-web``.
How to enable basic authorization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies 😊:
.. code-block:: shell
yay -S --asdeps python-aiohttp-security python-aiohttp-session python-cryptography
#.
Configure the service to enable authorization:
.. code-block:: ini
[auth]
target = configuration
salt = somerandomstring
The ``salt`` parameter is optional, but recommended, and can be set to any (random) string.
#.
In order to provide access for reporting from application instances you can (the recommended way) use unix sockets by the following configuration (note, that it requires ``python-requests-unixsocket2`` package to be installed):
.. code-block:: ini
[web]
unix_socket = /run/ahriman/ahriman-web.sock
This socket path must be available for web service instance and must be available for all application instances (e.g. in case if you are using docker container - see above - you need to make 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 -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, and finally configure the application:
.. code-block:: ini
[status]
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``.
Using PAM authentication
""""""""""""""""""""""""
There is also ability to allow system users to log in. To do so, the following configuration have to be set:
.. code-block:: ini
[auth]
target = pam
full_access_group = wheel
With this setup, every user (except root) will be able to log in by using system password. If user belongs to the ``wheel`` group, the full access will be automatically granted. It is also possible to manually add, block user or change user rights via usual user management process.
How to enable OAuth authorization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create OAuth web application, download its ``client_id`` and ``client_secret``.
#.
Guess what? Install dependencies:
.. code-block:: shell
yay -S --asdeps python-aiohttp-security python-aiohttp-session python-cryptography python-aioauth-client
#.
Configure the service:
.. code-block:: ini
[auth]
target = oauth
client_id = ...
client_secret = ...
[web]
address = https://example.com
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.
#.
If you are not going to use unix socket, you also need to create service user (remember to set ``auth.salt`` option before if required):
.. code-block:: shell
sudo -u ahriman ahriman user-add --as-service -r full api
#.
Create end-user:
.. 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``.
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``.

View File

@ -1,28 +1,28 @@
Welcome to ahriman's documentation!
===================================
Wrapper for managing custom repository inspired by `repo-scripts <https://github.com/arcan1s/repo-scripts>`_.
Wrapper for managing custom repository inspired by `repo-scripts <https://github.com/arcan1s/repo-scripts>`__.
Features
--------
* Install-configure-forget manager for the very own repository.
* Multi-architecture support.
* Multi architecture and repository support.
* Dependency manager.
* VCS packages support.
* Official repository support.
* Ability to patch AUR packages and even create package from local PKGBUILDs.
* Various rebuild options with ability to automatically bump package version.
* 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).
* 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>`_.
* `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 <https://repo.arcanis.me/x86_64/index.html>`__.
* `Telegram feed <https://t.me/arcanisrepo>`__.
Contents
--------
@ -33,7 +33,7 @@ Contents
setup
configuration
command-line
faq
faq/index
migration
architecture
advanced-usage

View File

@ -1,7 +1,7 @@
Manual migrations
=================
Normally most of migrations are handled automatically after application start. However, some upgrades require manual interventions; this document describes them.
Normally the most of migrations are handled automatically after application start, however, some upgrades require manual interventions; this document describes them.
Upgrades to breakpoints
-----------------------
@ -13,8 +13,8 @@ This release includes major upgrade for the newest devtools and archlinux reposi
#. Upgrade to the latest major release of python (3.11) (required by other changes).
#. Upgrade devtools to the latest release.
#. Backup your settings, ``/etc/ahriman.ini.d/00-setup-overrides.ini`` by default.
#. Run setup command (i.e. ``ahriman service-setup``) again with the same arguments as you used before. This step can be done manually by moving ``devtools`` configuration (something like ``/usr/share/devtools/pacman-ahriman*.conf``) to new location ``/usr/share/devtools/pacman.conf.d/`` under name ``ahriman.conf``. After that make sure to remove any ``community`` mentions from configurations (e.g. ``/usr/share/devtools/pacman.conf.d/ahriman.conf``, ``/etc/ahriman.ini``) if there were any. The only thing which will change is ``devtools`` configuration.
#. Backup local settings, ``/etc/ahriman.ini.d/00-setup-overrides.ini`` by default.
#. Run setup command (i.e. ``ahriman service-setup``) again with the same arguments as used before. This step can be done manually by moving ``devtools`` configuration (something like ``/usr/share/devtools/pacman-ahriman*.conf``) to new location ``/usr/share/devtools/pacman.conf.d/`` under name ``ahriman.conf``. After that make sure to remove any ``community`` mentions from configurations (e.g. ``/usr/share/devtools/pacman.conf.d/ahriman.conf``, ``/etc/ahriman.ini``) if there were any. The only thing which will change is ``devtools`` configuration.
#. Remove build chroot as it is incompatible, e.g. ``sudo ahriman service-clean --chroot``.
#. Run ``sudo -u ahriman ahriman update --no-aur --no-local --no-manual -yy`` in order to update local databases.
@ -27,7 +27,7 @@ This release includes paths migration. Unlike usual case, no automatic migration
* Path to pacman databases now includes repository name too, it has been changed from ``/var/lib/ahriman/pacman/x86_64`` to ``/var/lib/ahriman/pacman/aur-clone/x86_64``.
* Path to repository itself also includes repository name, from ``/var/lib/ahriman/repository/x86_64`` to ``/var/lib/ahriman/repository/aur-clone/x86_64``.
In order to migrate to new filesystem tree the following actions are required:
In order to migrate to the new filesystem tree the following actions are required:
#.
Stop and disable all services, e.g. timer and web service:
@ -38,13 +38,13 @@ In order to migrate to new filesystem tree the following actions are required:
sudo systemctl disable --now ahriman-web@x86_64
#.
Create directory tree. It can be done by running ``ahriman service-tree-migrate`` subcommand. It performs copying between the old repository tree and the new one. Alternatively you can copy directories by hands.
Create directory tree. It can be done by running ``ahriman service-tree-migrate`` subcommand. It performs copying between the old repository tree and the new one. Alternatively directories can be copied by hands.
#.
Edit configuration in case if anything is pointing to the old path, e.g. HTML report generation, in the way in which it will be pointed to directory inside repository specific one, e.g. ``/var/lib/ahriman/repository/x86_64`` to ``/var/lib/ahriman/repository/aur-clone/x86_64``.
Edit configuration in case if anything is pointing to the old path, e.g. HTML report generation, in the way in which it will point to the directory inside repository specific one, e.g. ``/var/lib/ahriman/repository/x86_64`` to ``/var/lib/ahriman/repository/aur-clone/x86_64``.
#.
Edit devtools pacman configuration (``/usr/share/devtools/pacman.conf.d/ahriman-x86_64.conf`` by default) replacing ``Server`` with path to your repository, e.g.:
Run setup command (i.e. ``ahriman service-setup``) again with the same arguments as used before. This step can be done manually by editing devtools pacman configuration (``/usr/share/devtools/pacman.conf.d/ahriman-x86_64.conf`` by default) replacing ``Server`` with path to the repository, e.g.:
.. code-block:: ini
@ -52,15 +52,15 @@ In order to migrate to new filesystem tree the following actions are required:
SigLevel = Optional TrustAll
Server = file:///var/lib/ahriman/repository/aur-clone/x86_64
Alternatively it can be done by running ``service-setup`` command again.
In case of manual interventions make sure to remove architecture reference from ``web`` sections (if any) to avoid ambiguity.
#.
Make sure to update remote synchronization services if any. Almost all of them rely on current repository tree by default, so you need to setup either redirects or configure to synchronize to the old locations (e.g. ``object_path`` option for S3 synchronization).
Make sure to update remote synchronization services if any. Almost all of them rely on current repository tree by default, so it is required to setup either redirects or configure to synchronize to the old locations (e.g. ``object_path`` option for S3 synchronization).
#.
Enable and start services again. Unit template parameter should include both repository architecture and name, dash separated, e.g. ``x86_64-aur-clone``:
Enable and start services again. Unit template parameter should include both repository architecture and name, dash separated, e.g. ``x86_64-aur-clone``, where ``x86_64`` is the repository architecture and ``aur-clone`` is the repository name:
.. code-block:: shell
sudo systemctl enable --now ahriman@x86_64-aur-clone.timer
sudo systemctl enable --now ahriman-web@x86_64-aur-clone
sudo systemctl enable --now ahriman-web

View File

@ -81,7 +81,7 @@ Initial setup
.. code-block:: shell
systemctl enable --now ahriman-web@x86_64-aur-clone
systemctl enable --now ahriman-web
#.
Add packages by using ``ahriman package-add {package}`` command:

View File

@ -1,7 +1,7 @@
Triggers
========
The package provides ability to write custom extensions which will be run on (the most) actions, e.g. after updates. By default ahriman provides three types of extensions - reporting, files uploading and PKGBUILD syncronization. Each extension must derive from the ``ahriman.core.triggers.Trigger`` class and should implement at least one of the abstract methods:
The package provides ability to write custom extensions which will be run on (the most) actions, e.g. after updates. By default ahriman provides three types of extensions - reporting, files uploading and PKGBUILD synchronization. Each extension must derive from the ``ahriman.core.triggers.Trigger`` class and should implement at least one of the abstract methods:
* ``on_result`` - trigger action which will be called after build process, the build result and the list of repository packages will be supplied as arguments.
* ``on_start`` - trigger action which will be called right before the start of the application process.
@ -14,6 +14,16 @@ Built-in triggers
For the configuration details and settings explanation kindly refer to the :doc:`documentation <configuration>`.
``ahriman.core.distributed.WorkerLoaderTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Special trigger to be used to load workers from database on the start of the application rather than configuration. If the option is already set, it will skip processing.
``ahriman.core.distributed.WorkerTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another trigger for the distributed system, which registers itself as remote worker, calling remote service periodically.
``ahriman.core.gitremote.RemotePullTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -39,7 +49,7 @@ Generator for keyring package. This trigger will extract keys from local keychai
``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.
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``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -83,9 +93,9 @@ The ``context`` also implements collection methods such as ``__iter__`` and ``__
Trigger example
---------------
Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`_, which provides easy HTTP API for integration triggers).
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``).
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``).
As it has been mentioned, our trigger must derive from specific class:
@ -139,4 +149,4 @@ After that run application as usual and receive notification in your slack chann
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.
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.

View File

@ -1,13 +1,13 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=2.11.0
pkgver=2.14.1
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')
url="https://github.com/arcan1s/ahriman"
license=('GPL3')
depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-pyelftools' 'python-requests' 'python-srcinfo')
makedepends=('python-build' 'python-flit' 'python-installer' 'python-wheel')
optdepends=('breezy: -bzr packages support'
'darcs: -darcs packages support'
@ -16,18 +16,17 @@ optdepends=('breezy: -bzr packages support'
'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-requests-unixsocket2: 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"
source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgname-$pkgver.tar.gz"
'ahriman.sysusers'
'ahriman.tmpfiles')
install="$pkgname.install"
@ -35,18 +34,18 @@ backup=('etc/ahriman.ini'
'etc/ahriman.ini.d/logging.ini')
build() {
cd "$pkgname"
cd "$pkgname-$pkgver"
python -m build --wheel --no-isolation
}
package() {
cd "$pkgname"
cd "$pkgname-$pkgver"
python -m installer --destdir="$pkgdir" "dist/$pkgname-$pkgver-py3-none-any.whl"
# thanks too PEP517, which we all wanted, you need to install data files manually nowadays
pushd package && find . -type f -exec install -Dm644 "{}" "$pkgdir/usr/{}" \; && popd
pushd package && find . \( -type f -or -type l \) -exec install -Dm644 "{}" "$pkgdir/usr/{}" \; && popd
# keep usr/share configs as reference and copy them to /etc
install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini"

View File

@ -13,30 +13,30 @@ post_upgrade() {
_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:
It was found that there was an upgrade 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;
* merge upstream configuration into local configuration (e.g. by using pacdiff);
* run setup command (i.e. ahriman service-setup) with the same arguments as did before;
* remove build chroot, e.g.: ahriman service-clean --chroot;
* update local databases: ahriman update --no-aur --no-local --no-manual -yy.
For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migration.html
For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migration.html.
EOF
}
_2_12_0_1_changes() {
cat << EOF
Whereas old tree is still supported it is highly recommended to migrate to the new one:
Whereas old local tree is still supported it is highly recommended to migrate to the new one:
* stop and disable all services;
* run service-tree-migrate as ahriman user;
* edit configuration to avoid pointing to the old paths;
* update devtools pacman configuration (e.g. /usr/share/devtools/pacman.conf.d/ahriman-x86_64.conf) with new repository path;
* update synchronization services in order to support new paths (or setup redirects);
* enable web and timer services again by using x86_64-aur-clone suffix, where x86_64 is your architecture and
aur-clone is repository name;
* edit local configuration to avoid pointing to the old paths;
* run setup command (i.e. ahriman service-setup) with the same arguments as did before;
* update remote services in order to support new paths (or setup redirects) if any;
* enable web and timer services again by using x86_64-aur-clone suffix,
where x86_64 is the repository architecture and aur-clone is the repository name.
For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migration.html
For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migration.html.
EOF
}

View File

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

View File

@ -0,0 +1,11 @@
[Unit]
Description=ArcH linux ReposItory MANager (%i)
[Service]
Type=simple
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-daemon --no-changes --refresh
User=ahriman
Group=ahriman
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,12 @@
[Unit]
Description=ArcH linux ReposItory MANager web server
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/ahriman web
User=ahriman
Group=ahriman
[Install]
WantedBy=multi-user.target

View File

@ -1,12 +0,0 @@
[Unit]
Description=ArcH linux ReposItory MANager web server (%i)
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/ahriman --repository-id "%I" web
User=ahriman
Group=ahriman
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1 @@
ahriman-web.service

View File

@ -2,6 +2,6 @@
Description=ArcH linux ReposItory MANager (%i)
[Service]
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-update --refresh
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-update --no-changes --refresh
User=ahriman
Group=ahriman
Group=ahriman

View File

@ -6,4 +6,4 @@ OnCalendar=daily
RandomizedDelaySec=3600
[Install]
WantedBy=timers.target
WantedBy=timers.target

View File

@ -1,85 +1,366 @@
[settings]
; Relative path to directory with configuration files overrides. Overrides will be applied in alphabetic order.
include = ahriman.ini.d
; Relative path to configuration used by logging package.
logging = ahriman.ini.d/logging.ini
apply_migrations = yes
; Perform database migrations on the application start. Do not touch this option unless you know what are you doing.
;apply_migrations = yes
; Path to the application SQLite database.
database = /var/lib/ahriman/ahriman.db
suppress_http_log_errors = yes
[alpm]
; Path to pacman system database cache.
database = /var/lib/pacman
; Arch linux mirror used by local pacman for synchronization.
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
; Space separated list of pacman repositories to search for packages.
repositories = core extra multilib
; Pacman's root directory. In the most cases it must point to the system root.
root = /
; Sync files databases too, which is required by deep dependencies check
sync_files_database = yes
; Use local packages cache. If this option is enabled, the service will be able to synchronize databases (available
; as additional option for some subcommands). If set to no, databases must be synchronized manually.
use_ahriman_cache = yes
[auth]
; Authentication provider, must be one of disabled, configuration, oauth.
target = disabled
max_age = 604800
oauth_provider = GoogleClient
oauth_scopes = https://www.googleapis.com/auth/userinfo.email
; Allow read-only endpoint to be called without authentication.
allow_read_only = yes
; OAuth2 application client ID and secret. Required if oauth is used.
;client_id =
;client_secret =
; Cookie secret key to be used for cookies encryption. Must be valid 32 bytes URL-safe base64-encoded string.
; If not set, it will be generated automatically.
;cookie_secret_key =
; Name of the secondary group to be used as admin group in the service.
;full_access_group = wheel
; Authentication cookie expiration in seconds.
;max_age = 604800
; OAuth2 provider icon for the web interface.
;oauth_icon = google
; OAuth2 provider class name, one of provided by aioauth-client. Required if oauth is used.
;oauth_provider = GoogleClient
; Scopes list for OAuth2 provider. Required if oauth is used.
;oauth_scopes = https://www.googleapis.com/auth/userinfo.email
; Allow login as root user (only if PAM is used).
;permit_root_login = no
; Optional password salt.
;salt =
[build]
archbuild_flags =
ignore_packages =
makechrootpkg_flags =
; List of paths to be used for implicit dependency scan
allowed_scan_paths = /usr/lib
; List of additional flags passed to archbuild command.
;archbuild_flags =
; List of paths to be excluded for implicit dependency scan. Usually they should be subpaths of allowed_scan_paths
blacklisted_scan_paths = /usr/lib/cmake
; Path to build command
;build_command =
; List of packages to be ignored during automatic updates.
;ignore_packages =
; Include debug packages
;include_debug_packages = yes
; List of additional flags passed to makechrootpkg command.
;makechrootpkg_flags =
; List of additional flags passed to makepkg command.
makepkg_flags = --nocolor --ignorearch
; List of enabled triggers in the order of calls.
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
; List of well-known triggers. Used only for configuration purposes.
triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distributed.WorkerRegisterTrigger ahriman.core.distributed.WorkerTrigger ahriman.core.distributed.WorkerUnregisterTrigger ahriman.core.gitremote.RemotePullTrigger ahriman.core.gitremote.RemotePushTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.support.KeyringTrigger ahriman.core.support.MirrorlistTrigger
; Maximal age in seconds of the VCS packages before their version will be updated with its remote source.
;vcs_allowed_age = 604800
; List of worker nodes addresses used for build process, e.g.:
; workers = http://10.0.0.1:8080 http://10.0.0.3:8080
; Empty list means run on the local instance.
;workers =
[repository]
; Application root.
root = /var/lib/ahriman
[sign]
; Enable repository or package signing. Must be one of repository, package.
target =
; PGP key used for signing as default.
;key =
[keyring]
target =
[mirrorlist]
target =
[remote-pull]
target =
[remote-push]
target =
[report]
target = console
[console]
use_utf = yes
[email]
no_empty_report = yes
template = email-index.jinja2
templates = /usr/share/ahriman/templates
ssl = disabled
[html]
template = repo-index.jinja2
templates = /usr/share/ahriman/templates
[telegram]
template = telegram-index.jinja2
templates = /usr/share/ahriman/templates
[upload]
target =
[rsync]
command = rsync --archive --compress --partial --delete
[s3]
chunk_size = 8388608
[status]
; Global switch to enable or disable status reporting.
enabled = yes
; Address of the remote service, e.g.:
; address = http://1.0.0.1:8080
; In case if unix sockets are used, it might point to the valid socket with encoded path, e.g.:
; address = http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket
;address =
; Optional password for authentication (if enabled).
;password =
; Do not log HTTP errors if occurs.
suppress_http_log_errors = yes
; HTTP request timeout in seconds.
;timeout = 30
; Optional username for authentication (if enabled).
;username =
[web]
debug = no
debug_check_host = no
debug_allowed_hosts =
; External address of the web service. Will be used for some features like OAuth. If none set will be generated as
; address = http://web.host:web.port
;address =
; Enable file upload endpoint used by some triggers.
;enable_archive_upload = no
; Address to bind the server.
host = 127.0.0.1
; Full URL to the repository index page used by templates.
;index_url =
; Max file size in bytes which can be uploaded to the server.
;max_body_size =
; Port to listen. Must be set, if the web service is enabled.
;port =
; Disable status (e.g. package status, logs, etc) endpoints. Useful for build only modes.
;service_only = no
; Path to directory with static files.
static_path = /usr/share/ahriman/templates/static
; List of directories with templates.
templates = /usr/share/ahriman/templates
unix_socket_unsafe = yes
; Path to unix socket. If none set, unix socket will be disabled.
;unix_socket =
; Allow unix socket to be world readable.
;unix_socket_unsafe = yes
; Maximum amount of time in seconds to be waited before lock will be free, used by spawned processes (0 is infinite).
;wait_timeout =
[keyring]
; List of configuration section names for keyring generator plugin, e.g.:
; target = keyring-trigger
target =
; Keyring generator trigger sample.
;[keyring-trigger]
; Generator type name.
;type = keyring-generator
; Optional keyring package description.
;description=
; Optional URL to the repository homepage.
;homepage=
; Keyring package licenses list.
;license = Unlicense
; Optional keyring package name.
;package =
; Optional packager PGP keys list. If none set, it will read from database.
;packagers =
; List of revoked PGP keys.
;revoked =
; List of master PGP keys. If none set, the sign.key value will be used.
;trusted =
[mirrorlist]
; List of configuration section names for mirrorlist generator plugin, e.g.:
; target = mirrorlist-trigger
target =
; Mirror list generator trigger sample.
;[mirrorlist-trigger]
; Generator type name.
;type = mirrorlist-generator
; Optional mirrorlist package description.
;description=
; Optional URL to the repository homepage.
;homepage=
; Mirrorlist package licenses list.
;license = Unlicense
; Optional mirrorlist package name.
;package =
; Absolute path to generated mirrorlist file, usually path inside /etc/pacman.d directory.
;path =
; List of repository mirrors.
;servers =
[remote-pull]
; List of configuration section names for git remote pull plugin, e.g.:
; target = remote-pull-trigger
target =
; git remote pull trigger sample.
;[remote-pull-trigger]
; Valid URL to pull repository, e.g.:
; pull_url = https://github.com/arcan1s/arcanisrepo.git
;pull_url =
; Remote branch to pull.
;pull_branch = master
[remote-push]
; List of configuration section names for git remote push plugin, e.g.:
; target = remote-push-trigger
target =
; git remote push trigger sample.
;[remote-push-trigger]
; Author commit email.
;commit_email = ahriman@localhost
; Author commit user.
;commit_user = ahriman
; Valid URL to push repository, e.g.:
; push_url = https://key:token@github.com/arcan1s/arcanisrepo.git
; Note, that more likely authentication must be enabled.
;push_url =
; Remote branch to push.
;push_branch = master
[report]
; List of configuration section names for reporting plugin.
target = console
; Console reporting trigger configuration sample.
[console]
; Trigger type name
;type = console
; Use utf8 symbols in output.
use_utf = yes
; Email reporting trigger configuration sample.
[email]
; Trigger type name
;type = email
; Optional URL to the repository homepage.
;homepage=
; SMTP server address.
;host =
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Skip report generation if no packages were updated.
;no_empty_report = yes
; SMTP password.
;password =
; SMTP server port.
;port =
; List of emails to receive the reports.
;receivers =
; Sender email.
;sender =
; SMTP server SSL mode, one of ssl, starttls, disabled.
;ssl = disabled
; Template name to be used.
template = email-index.jinja2
; Template name to be used for full packages list generation (same as HTML report).
;template_full =
; List of directories with templates.
templates = /usr/share/ahriman/templates
; SMTP user.
;user =
; HTML reporting trigger configuration sample.
[html]
; Trigger type name
;type = html
; Optional URL to the repository homepage.
;homepage=
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Output path for the HTML report.
;path =
; Template name to be used.
template = repo-index.jinja2
; List of directories with templates.
templates = /usr/share/ahriman/templates
; Remote service callback trigger configuration sample.
[remote-call]
; Trigger type name
;type = remote-call
; Call for AUR packages update.
;aur = no
; Call for local packages update.
;local = no
; Call for manual packages update.
;manual = no
; Wait until remote process will be terminated in seconds.
;wait_timeout = -1
; Telegram reporting trigger configuration sample.
[telegram]
; Trigger type name
;type = telegram
; Telegram bot API key.
;api_key =
; Telegram chat ID.
;chat_id =
; Optional URL to the repository homepage.
;homepage=
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Template name to be used.
template = telegram-index.jinja2
; Telegram specific template mode, one of MarkdownV2, HTML or Markdown.
;template_type = HTML
; List of directories with templates.
templates = /usr/share/ahriman/templates
; HTTP request timeout in seconds.
;timeout = 30
[upload]
; List of configuration section names for remote upload plugin, e.g.:
; target = rsync s3
target =
; GitHub upload trigger configuration sample.
[github]
; Trigger type name
;type = github
; GitHub repository owner username.
;owner =
; GitHub API key. public_repo (repo) scope is required.
;password =
; GitHub repository name.
;repository =
; HTTP request timeout in seconds.
;timeout = 30
; Include repository name to release name (recommended).
;use_full_release_name = no
; GitHub authentication username.
;username =
; Remote instance upload trigger configuration sample.
[remote-service]
; Trigger type name
;type = remote-service
; HTTP request timeout in seconds.
;timeout = 30
; rsync upload trigger configuration sample.
[rsync]
; Trigger type name
;type = rsync
; rsync command to run.
command = rsync --archive --compress --partial --delete
; Remote address and directory to sync, e.g.:
; remote = ahriman@10.0.0.1:/srv/repo
;remote =
; S3 upload trigger configuration sample.
[s3]
; Trigger type name
;type = s3
; AWS services access key.
;access_key =
; AWS S3 bucket name.
;bucket =
; Chunk size tp calculate ETags. Do not edit this value.
;chunk_size = 8388608
; Optional path prefix for stored objects.
;object_path =
; AWS S3 bucket region.
;region =
; AWS services secret key.
;secret_key =
; Remote worker configuration sample.
;[worker]
; Remotely reachable address of this instance, e.g.:
; address = http://10.0.0.1:8080
;address =
; Unique identifier of this instance if any.
;identifier =
; Maximum amount of time in seconds after which worker will be considered offline in case of no reports.
;time_to_live = 60

View File

@ -36,7 +36,7 @@ level = DEBUG
qualname = root
[logger_http]
level = DEBUG
level = WARNING
qualname = http
propagate = 0

View File

@ -6,10 +6,8 @@
<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">
<script src="https://cdn.jsdelivr.net/npm/@stoplight/elements@7.13.7/web-components.min.js" crossorigin="anonymous" type="application/javascript"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stoplight/elements@7.13.7/styles.min.css" crossorigin="anonymous" type="text/css">
</head>
<body>

View File

@ -1,12 +1,10 @@
<!doctype html>
<html lang="en">
<head>
<title>{{ repository }}</title>
<title>ahriman</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/static/favicon.ico">
{% include "utils/style.jinja2" %}
{% include "user-style.jinja2" ignore missing %}
</head>
@ -16,7 +14,22 @@
{% include "utils/bootstrap-scripts.jinja2" %}
<div class="container">
<h1 id="badge-repository">ahriman</h1>
<nav class="navbar navbar-expand-lg">
<div class="navbar-brand"><img src="/static/logo.svg" width="30" height="30" alt=""></div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#repositories-navbar-supported-content" aria-controls="repositories-navbar-supported-content" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div id="repositories-navbar-supported-content" class="collapse navbar-collapse">
<ul id="repositories" class="nav nav-tabs">
{% for repository in repositories %}
<li class="nav-item">
<a id="{{ repository.id }}-link" class="nav-link" href="#{{ repository.id }}" data-repository="{{ repository.repository }}" data-architecture="{{ repository.architecture }}">{{ repository.repository }} ({{ repository.architecture }})</a>
</li>
{% endfor %}
</ul>
</div>
</nav>
</div>
<div id="alert-placeholder" class="toast-container p3 top-0 start-50 translate-middle-x"></div>
@ -27,42 +40,43 @@
{% if not auth.enabled or auth.username is not none %}
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-box"></i> packages
<i class="bi bi-box"></i><span class="d-none d-sm-inline"> packages</span>
</button>
<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>
<button id="package-add-button" 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>
<button id="package-update-button" class="btn dropdown-item" onclick="packagesUpdate()" 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>
<button id="package-rebuild-button" 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>
<button id="package-remove-button" class="btn dropdown-item" onclick="packagesRemove()" 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 id="key-import-button" type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#key-import-modal" hidden>
<i class="bi bi-key"></i><span class="d-none d-sm-inline"> import key</span>
</button>
{% endif %}
<button type="button" class="btn btn-secondary" onclick="reload()">
<i class="bi bi-arrow-clockwise"></i> reload
<i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span>
</button>
</div>
<table id="packages" class="table table-striped table-hover"
<table id="packages"
data-classes="table table-hover"
data-export-options='{"fileName": "packages"}'
data-filter-control="true"
data-filter-control-visible="false"
@ -87,13 +101,13 @@
<tr>
<th data-checkbox="true"></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-align="right" 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>
<th data-sortable="true" data-align="right" 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-align="center" data-cell-style="statusFormat" data-field="status" data-filter-control="select" data-filter-control-placeholder="(any status)">status</th>
</tr>
</thead>
</table>
@ -117,7 +131,7 @@
{% if auth.enabled %}
<ul class="nav">
{% if auth.username is none %}
<li>{{ auth.control|safe }}</li>
<li>{{ auth.control | safe }}</li>
{% else %}
<li>
<form action="/api/v1/logout" method="post">

View File

@ -1,7 +1,7 @@
<script>
const alertPlaceholder = $("#alert-placeholder");
function createAlert(title, message, clz) {
function createAlert(title, message, clz, action) {
const wrapper = document.createElement("div");
wrapper.classList.add("toast", clz);
wrapper.role = "alert";
@ -21,9 +21,9 @@
alertPlaceholder.append(wrapper);
const toast = new bootstrap.Toast(wrapper);
wrapper.addEventListener("hidden.bs.toast", () => {
wrapper.addEventListener("hidden.bs.toast", _ => {
wrapper.remove(); // bootstrap doesn't remove elements
reload();
(action || reload)();
});
toast.show();
}
@ -38,8 +38,8 @@
createAlert(title, description(details), "text-bg-danger");
}
function showSuccess(title, description) {
createAlert(title, description, "text-bg-success");
function showSuccess(title, description, action) {
createAlert(title, description, "text-bg-success", action);
}
</script>

View File

@ -8,21 +8,21 @@
</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>
<label for="key-import-fingerprint-input" class="col-2 col-form-label">fingerprint</label>
<div class="col-10">
<input id="key-import-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>
<label for="key-import-server-input" class="col-2 col-form-label">key server</label>
<div class="col-10">
<input id="key-import-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 class="col-2"></div>
<div class="col-10">
<pre class="language-less"><samp id="key-import-body-input" class="pre-scrollable language-less"></samp><button id="key-import-copy-button" type="button" class="btn language-less" onclick="copyPgpKey()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
</div>
</div>
@ -38,25 +38,21 @@
<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 keyImportBodyInput = $("#key-import-body-input");
const keyImportCopyButton = $("#key-import-copy-button");
const keyFingerprintInput = $("#key-fingerprint-input");
const keyServerInput = $("#key-server-input");
const keyImportFingerprintInput = $("#key-import-fingerprint-input");
const keyImportServerInput = $("#key-import-server-input");
async function copyPgpKey() {
const logs = keyBodyInput.text();
await copyToClipboard(logs, keyCopyButton);
const logs = keyImportBodyInput.text();
await copyToClipboard(logs, keyImportCopyButton);
}
function fetchPgpKey() {
const key = keyFingerprintInput.val();
const server = keyServerInput.val();
const key = keyImportFingerprintInput.val();
const server = keyImportServerInput.val();
if (key && server) {
$.ajax({
@ -64,14 +60,14 @@
data: {"key": key, "server": server},
type: "GET",
dataType: "json",
success: response => { keyBodyInput.text(response.key); },
success: response => { keyImportBodyInput.text(response.key); },
});
}
}
function importPgpKey() {
const key = keyFingerprintInput.val();
const server = keyServerInput.val();
const key = keyImportFingerprintInput.val();
const server = keyImportServerInput.val();
if (key && server) {
$.ajax({
@ -90,4 +86,11 @@
});
}
}
$(_ => {
keyImportModal.on("hidden.bs.modal", _ => {
keyImportBodyInput.text("");
keyImportForm.trigger("reset");
});
});
</script>

View File

@ -1,32 +1,32 @@
<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">
<form id="login-form" onsubmit="return false">
<div class="modal-header">
<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">
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">username</label>
<div class="col-sm-10">
<input id="username" type="text" class="form-control" placeholder="enter username" name="username" required>
<label for="login-username" class="col-4 col-form-label">username</label>
<div class="col-8">
<input id="login-username" type="text" class="form-control" placeholder="enter username" name="username" required>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">password</label>
<div class="col-sm-10">
<label for="login-password" class="col-4 col-form-label">password</label>
<div class="col-8">
<div class="input-group">
<input id="password" type="password" class="form-control" placeholder="enter password" name="password" required>
<input id="login-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>
<button class="btn btn-outline-secondary" type="button" onclick="showPassword()"><i id="login-show-hide-password-button" class="bi bi-eye"></i></button>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary"><i class="bi bi-person"></i> login</button>
<button type="submit" class="btn btn-primary" onclick="login()"><i class="bi bi-person"></i> login</button>
</div>
</form>
</div>
@ -34,18 +34,53 @@
</div>
<script>
const passwordInput = $("#password");
const showHidePasswordButton = $("#show-hide-password-btn");
const loginModal = $("#login-modal");
const loginForm = $("#login-form");
const loginPasswordInput = $("#login-password");
const loginUsernameInput = $("#login-username");
const showHidePasswordButton = $("#login-show-hide-password-button");
function login() {
const password = loginPasswordInput.val();
const username = loginUsernameInput.val();
if (username && password) {
$.ajax({
url: "/api/v1/login",
data: JSON.stringify({username: username, password: password}),
type: "POST",
contentType: "application/json",
success: _ => {
loginModal.modal("hide");
showSuccess("Logged in", `Successfully logged in as ${username}`, _ => location.href = "/");
},
error: (jqXHR, _, errorThrown) => {
const message = _ =>
username === "admin" && password === "admin"
? "You've entered a password for user \"root\", did you make a typo in username?"
: `Could not login as ${username}`;
showFailure("Login error", message, jqXHR, errorThrown);
},
});
}
}
function showPassword() {
if (passwordInput.attr("type") === "password") {
passwordInput.attr("type", "text");
if (loginPasswordInput.attr("type") === "password") {
loginPasswordInput.attr("type", "text");
showHidePasswordButton.removeClass("bi-eye");
showHidePasswordButton.addClass("bi-eye-slash");
} else {
passwordInput.attr("type", "password");
loginPasswordInput.attr("type", "password");
showHidePasswordButton.removeClass("bi-eye-slash");
showHidePasswordButton.addClass("bi-eye");
}
}
$(_ => {
loginModal.on("hidden.bs.modal", _ => {
loginForm.trigger("reset");
});
});
</script>

View File

@ -1,5 +1,5 @@
<div id="package-add-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<form id="package-add-form" onsubmit="return false">
<div class="modal-header">
@ -8,12 +8,28 @@
</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>
<label for="package-add-repository-input" class="col-3 col-form-label">repository</label>
<div class="col-9">
<select id="package-add-repository-input" class="form-control" required>
{% for repository in repositories %}
<option value="{{ repository.id }}" data-repository="{{ repository.repository }}" data-architecture="{{ repository.architecture }}">{{ repository.repository }} ({{ repository.architecture }})</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row">
<label for="package-add-input" class="col-3 col-form-label">package</label>
<div class="col-9">
<input id="package-add-input" type="text" list="package-add-known-packages-dlist" autocomplete="off" class="form-control" placeholder="AUR package" required>
<datalist id="package-add-known-packages-dlist"></datalist>
</div>
</div>
<div class="form-group row">
<div class="col-12">
<button id="package-add-variable-button" type="button" class="form-control btn btn-light rounded" onclick="packageAddVariableInputCreate()"><i class="bi bi-plus"></i> add environment variable </button>
</div>
</div>
<div id="package-add-variables-div" class="form-group row"></div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" onclick="packagesAdd()"><i class="bi bi-play"></i> add</button>
@ -27,52 +43,121 @@
<script>
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();
const packageAddInput = $("#package-add-input");
const packageAddRepositoryInput = $("#package-add-repository-input");
const packageAddKnownPackagesList = $("#package-add-known-packages-dlist");
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));
});
const packageAddVariablesDiv = $("#package-add-variables-div");
function packageAddVariableInputCreate() {
const variableInput = document.createElement("div");
variableInput.classList.add("input-group");
variableInput.classList.add("package-add-variable");
const variableNameInput = document.createElement("input");
variableNameInput.type = "text";
variableNameInput.classList.add("form-control");
variableNameInput.classList.add("package-add-variable-name");
variableNameInput.placeholder = "name";
variableNameInput.ariaLabel = "variable name";
const variableSeparator = document.createElement("span");
variableSeparator.classList.add("input-group-text")
variableSeparator.textContent = "=";
const variableValueInput = document.createElement("input");
variableValueInput.type = "text";
variableValueInput.classList.add("form-control");
variableValueInput.classList.add("package-add-variable-value");
variableValueInput.placeholder = "value";
variableValueInput.ariaLabel = "variable value";
const variableButtonRemove = document.createElement("button");
variableButtonRemove.type = "button";
variableButtonRemove.classList.add("btn");
variableButtonRemove.classList.add("btn-outline-danger");
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
variableButtonRemove.onclick = _ => { return variableInput.remove(); };
// bring them together
variableInput.appendChild(variableNameInput);
variableInput.appendChild(variableSeparator);
variableInput.appendChild(variableValueInput);
variableInput.appendChild(variableButtonRemove);
packageAddVariablesDiv.append(variableInput);
}
function patchesParse() {
const patches = packageAddVariablesDiv.find(".package-add-variable").map((_, element) => {
const richElement = $(element);
return {
key: richElement.find(".package-add-variable-name").val(),
value: richElement.find(".package-add-variable-value").val(),
};
}).filter((_, patch) => patch.key).get();
return {patches: patches};
}
function packagesAdd(packages, patches, repository) {
packages = packages ?? packageAddInput.val();
patches = patches ?? patchesParse();
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
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);
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, patches);
}
}
function packagesRequest() {
const packages = packageInput.val();
function packagesRequest(packages, patches) {
packages = packages ?? packageAddInput.val();
patches = patches ?? patchesParse();
const repository = getRepositorySelector(packageAddRepositoryInput);
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);
doPackageAction("/api/v1/service/request", [packages], repository, onSuccess, onFailure, patches);
}
}
$(_ => {
packageAddModal.on("shown.bs.modal", _ => {
$(`#package-add-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
});
packageAddModal.on("hidden.bs.modal", _ => {
packageAddVariablesDiv.empty();
packageAddForm.trigger("reset");
});
packageAddInput.keyup(_ => {
clearTimeout(packageAddInput.data("timeout"));
packageAddInput.data("timeout", setTimeout($.proxy(_ => {
const value = packageAddInput.val();
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;
});
packageAddKnownPackagesList.empty().append(options);
},
});
}
}, this), 500));
});
});
</script>

View File

@ -6,11 +6,63 @@
<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 class="form-group row mt-2">
<div class="col-4 col-lg-1" style="text-align: right">version</div>
<div id="package-info-version" class="col-8 col-lg-5"></div>
<div class="col-4 col-lg-1" style="text-align: right">packager</div>
<div id="package-info-packager" class="col-8 col-lg-5"></div>
</div>
<div class="form-group row mt-2">
<div class="col-4 col-lg-1" style="text-align: right">groups</div>
<div id="package-info-groups" class="col-8 col-lg-5"></div>
<div class="col-4 col-lg-1" style="text-align: right">licenses</div>
<div id="package-info-licenses" class="col-8 col-lg-5"></div>
</div>
<div class="form-group row mt-2">
<div class="col-4 col-lg-1" style="text-align: right">upstream</div>
<div id="package-info-upstream-url" class="col-8 col-lg-5"></div>
<div class="col-4 col-lg-1" style="text-align: right">AUR</div>
<div id="package-info-aur-url" class="col-8 col-lg-5"></div>
</div>
<div class="form-group row mt-2">
<div class="col-4 col-lg-1" style="text-align: right">packages</div>
<div id="package-info-packages" class="col-8 col-lg-5"></div>
<div class="col-4 col-lg-1" style="text-align: right">depends</div>
<div id="package-info-depends" class="col-8 col-lg-5"></div>
</div>
<hr class="col-12">
<div id="package-info-variables-block" hidden>
<h3>Environment variables</h3>
<div id="package-info-variables-div" class="form-group row"></div>
<hr class="col-12">
</div>
<nav>
<div class="nav nav-tabs" role="tablist">
<button id="package-info-logs-button" class="nav-link active" data-bs-toggle="tab" data-bs-target="#package-info-logs" type="button" role="tab" aria-controls="package-info-logs" aria-selected="true"><h3>Build logs</h3></button>
<button id="package-info-changes-button" class="nav-link" data-bs-toggle="tab" data-bs-target="#package-info-changes" type="button" role="tab" aria-controls="package-info-changes" aria-selected="false"><h3>Changes</h3></button>
</div>
</nav>
<div class="tab-content" id="nav-tabContent">
<div id="package-info-logs" class="tab-pane fade show active" role="tabpanel" aria-labelledby="package-info-logs-button" tabindex="0">
<pre class="language-console"><code id="package-info-logs-input" class="pre-scrollable language-console"></code><button id="package-info-logs-copy-button" type="button" class="btn language-console" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
<div id="package-info-changes" class="tab-pane fade" role="tabpanel" aria-labelledby="package-info-changes-button" tabindex="0">
<pre class="language-diff"><code id="package-info-changes-input" class="pre-scrollable language-diff"></code><button id="package-info-changes-copy-button" type="button" class="btn language-diff" onclick="copyChanges()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
</div>
</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>
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal" hidden><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
<button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal" hidden><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button>
<button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
</div>
</div>
</div>
@ -22,20 +74,118 @@
const packageInfo = $("#package-info");
const packageInfoLogsInput = $("#package-info-logs-input");
const packageInfoLogsCopyButton = $("#logs-copy-btn");
const packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
const packageInfoChangesInput = $("#package-info-changes-input");
const packageInfoChangesCopyButton = $("#package-info-changes-copy-button");
const packageInfoAurUrl = $("#package-info-aur-url");
const packageInfoDepends = $("#package-info-depends");
const packageInfoGroups = $("#package-info-groups");
const packageInfoLicenses = $("#package-info-licenses");
const packageInfoPackager = $("#package-info-packager");
const packageInfoPackages = $("#package-info-packages");
const packageInfoUpstreamUrl = $("#package-info-upstream-url");
const packageInfoVersion = $("#package-info-version");
const packageInfoVariablesBlock = $("#package-info-variables-block");
const packageInfoVariablesDiv = $("#package-info-variables-div");
async function copyChanges() {
const changes = packageInfoChangesInput.text();
await copyToClipboard(changes, packageInfoChangesCopyButton);
}
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
function hideInfoControls(hidden) {
packageInfoRemoveButton.attr("hidden", hidden);
packageInfoUpdateButton.attr("hidden", hidden);
}
function insertVariable(packageBase, variable) {
const variableInput = document.createElement("div");
variableInput.classList.add("input-group");
const variableNameInput = document.createElement("input");
variableNameInput.classList.add("form-control");
variableNameInput.readOnly = true;
variableNameInput.value = variable.key;
const variableSeparator = document.createElement("span");
variableSeparator.classList.add("input-group-text")
variableSeparator.textContent = "=";
const variableValueInput = document.createElement("input");
variableValueInput.classList.add("form-control");
variableValueInput.readOnly = true;
variableValueInput.value = variable.value;
const variableButtonRemove = document.createElement("button");
variableButtonRemove.type = "button";
variableButtonRemove.classList.add("btn");
variableButtonRemove.classList.add("btn-outline-danger");
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
variableButtonRemove.onclick = _ => {
$.ajax({
url: `/api/v1/packages/${packageBase}/patches/${variable.key}`,
type: "DELETE",
dataType: "json",
success: _ => variableInput.remove(),
});
};
// bring them together
variableInput.appendChild(variableNameInput);
variableInput.appendChild(variableSeparator);
variableInput.appendChild(variableValueInput);
variableInput.appendChild(variableButtonRemove);
packageInfoVariablesDiv.append(variableInput);
}
function loadChanges(packageBase, onFailure) {
$.ajax({
url: `/api/v1/packages/${packageBase}/changes`,
data: {
architecture: repository.architecture,
repository: repository.repository,
},
type: "GET",
dataType: "json",
success: response => {
const changes = response.changes;
packageInfoChangesInput.text(changes || "");
packageInfoChangesInput.map((_, el) => hljs.highlightElement(el));
},
error: onFailure,
});
}
function loadLogs(packageBase, onFailure) {
$.ajax({
url: `/api/v2/packages/${packageBase}/logs`,
data: {
architecture: repository.architecture,
repository: repository.repository,
},
type: "GET",
dataType: "json",
success: response => {
const logs = response.map(log_record => {
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
});
packageInfoLogsInput.text(logs.join("\n"));
packageInfoLogsInput.map((_, el) => hljs.highlightElement(el));
},
error: onFailure,
});
}
function loadPackage(packageBase, onFailure) {
const headerClass = status => {
if (status === "pending") return ["bg-warning"];
if (status === "building") return ["bg-warning"];
@ -45,30 +195,121 @@
};
$.ajax({
url: `/api/v2/packages/${packageBase}/logs`,
url: `/api/v1/packages/${packageBase}`,
data: {
architecture: repository.architecture,
repository: repository.repository,
},
type: "GET",
dataType: "json",
success: response => {
packageInfo.text(`${response.package_base} ${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`);
const logs = response.logs.map(log_record => {
const [timestamp, record] = log_record;
return `[${new Date(1000 * timestamp).toISOString()}] ${record}`;
});
packageInfoLogsInput.text(logs.join("\n"));
const description = response.find(Boolean);
const packages = Object.keys(description.package.packages);
const aurUrl = description.package.remote.web_url;
const upstreamUrls = Array.from(
new Set(
Object.values(description.package.packages)
.map(single => single.url)
)
).sort();
packageInfo.text(`${description.package.base} ${description.status.status} at ${new Date(1000 * description.status.timestamp).toISOStringShort()}`);
packageInfoModalHeader.removeClass();
packageInfoModalHeader.addClass("modal-header");
headerClass(response.status.status).forEach((clz) => packageInfoModalHeader.addClass(clz));
headerClass(description.status.status).forEach(clz => packageInfoModalHeader.addClass(clz));
if (isPackageBaseSet) packageInfoModal.modal("show"); // we don't need to show window again
packageInfoAurUrl.html(aurUrl ? safeLink(aurUrl, aurUrl, "AUR link").outerHTML : "");
packageInfoDepends.html(listToTable(
Object.values(description.package.packages)
.reduce((accumulator, currentValue) => {
return accumulator.concat(currentValue.depends.filter(v => packages.indexOf(v) === -1))
.concat(currentValue.make_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (make)`))
.concat(currentValue.opt_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (optional)`));
}, [])
));
packageInfoGroups.html(listToTable(extractListProperties(description.package, "groups")));
packageInfoLicenses.html(listToTable(extractListProperties(description.package, "licenses")));
packageInfoPackager.text(description.package.packager);
packageInfoPackages.html(listToTable(packages));
packageInfoUpstreamUrl.html(upstreamUrls.map(url => safeLink(url, url, "upstream link").outerHTML).join("<br>"));
packageInfoVersion.text(description.package.version);
hideInfoControls(false);
},
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);
}
hideInfoControls(true);
onFailure(jqXHR, null, errorThrown);
},
});
}
function loadPatches(packageBase, onFailure) {
$.ajax({
url: `/api/v1/packages/${packageBase}/patches`,
type: "GET",
dataType: "json",
success: response => {
packageInfoVariablesDiv.empty();
response.map(patch => insertVariable(packageBase, patch));
packageInfoVariablesBlock.attr("hidden", response.length === 0);
},
error: onFailure,
});
}
function packageInfoRemove() {
const packageBase = packageInfoModal.data("package");
if (packageBase) return packagesRemove([packageBase]);
}
function packageInfoUpdate() {
const packageBase = packageInfoModal.data("package");
if (packageBase) return packagesAdd(packageBase, [], repository);
}
function showPackageInfo(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 onFailure = (jqXHR, _, errorThrown) => {
if (isPackageBaseSet) {
const message = error => `Could not load package ${packageBase} info: ${error}`;
showFailure("Load failure", message, jqXHR, errorThrown);
}
};
loadPackage(packageBase, onFailure);
loadPatches(packageBase, onFailure);
loadLogs(packageBase, onFailure);
loadChanges(packageBase, onFailure)
if (isPackageBaseSet) packageInfoModal.modal("show");
}
$(_ => {
packageInfoModal.on("hidden.bs.modal", _ => {
packageInfoAurUrl.empty();
packageInfoDepends.empty();
packageInfoGroups.empty();
packageInfoLicenses.empty();
packageInfoPackager.empty();
packageInfoPackages.empty();
packageInfoUpstreamUrl.empty();
packageInfoVersion.empty();
packageInfoVariablesBlock.attr("hidden", true);
packageInfoVariablesDiv.empty();
packageInfoLogsInput.empty();
packageInfoChangesInput.empty();
packageInfoModal.trigger("reset");
hideInfoControls(true);
});
});
</script>

View File

@ -1,5 +1,5 @@
<div id="package-rebuild-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<form id="package-rebuild-form" onsubmit="return false">
<div class="modal-header">
@ -8,9 +8,19 @@
</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>
<label for="package-rebuild-repository-input" class="col-3 col-form-label">repository</label>
<div class="col-9">
<select id="package-rebuild-repository-input" class="form-control" name="repository" required>
{% for repository in repositories %}
<option value="{{ repository.id }}" data-repository="{{ repository.repository }}" data-architecture="{{ repository.architecture }}">{{ repository.repository }} ({{ repository.architecture }})</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row">
<label for="package-rebuild-dependency-input" class="col-3 col-form-label">dependency</label>
<div class="col-9">
<input id="package-rebuild-dependency-input" type="text" class="form-control" placeholder="packages dependency" name="package" required>
</div>
</div>
</div>
@ -25,17 +35,26 @@
<script>
const packageRebuildModal = $("#package-rebuild-modal");
const packageRebuildForm = $("#package-rebuild-form");
packageRebuildModal.on("hidden.bs.modal", () => { packageRebuildForm.trigger("reset"); });
const dependencyInput = $("#dependency-input");
const packageRebuildDependencyInput = $("#package-rebuild-dependency-input");
const packageRebuildRepositoryInput = $("#package-rebuild-repository-input");
function packagesRebuild() {
const packages = dependencyInput.val();
const packages = packageRebuildDependencyInput.val();
const repository = getRepositorySelector(packageRebuildRepositoryInput);
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);
doPackageAction("/api/v1/service/rebuild", [packages], repository, onSuccess, onFailure);
}
}
$(_ => {
packageRebuildModal.on("shown.bs.modal", _ => {
$(`#package-rebuild-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
});
packageRebuildModal.on("hidden.bs.modal", _ => { packageRebuildForm.trigger("reset"); });
});
</script>

View File

@ -1,48 +1,29 @@
<script>
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 keyImportButton = $("#key-import-button");
const packageAddButton = $("#package-add-button");
const packageRebuildButton = $("#package-rebuild-button");
const packageRemoveButton = $("#package-remove-button");
const packageUpdateButton = $("#package-update-button");
const packageInfoRemoveButton = $("#package-info-remove-button");
const packageInfoUpdateButton = $("#package-info-update-button");
let repository = null;
const table = $("#packages");
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",
},
});
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, successText, failureText) {
function doPackageAction(uri, packages, repository, successText, failureText, data) {
const queryParams = $.param({
architecture: repository.architecture,
repository: repository.repository,
}); // it will never be empty btw
$.ajax({
url: uri,
data: JSON.stringify({packages: packages}),
url: `${uri}?${queryParams}`,
data: JSON.stringify(Object.assign({}, {packages: packages}, data || {})),
type: "POST",
contentType: "application/json",
success: _ => {
@ -55,25 +36,30 @@
});
}
function filterListGroups() {
return extractDataList(table.bootstrapTable("getData"), "groups");
}
function filterListLicenses() {
return extractDataList(table.bootstrapTable("getData"), "licenses");
}
function filterListPackagers() {
return extractDataList(table.bootstrapTable("getData"), "packager");
}
function getRepositorySelector(selector) {
const selected = selector.find(":selected");
return {
architecture: selected.data("architecture"),
repository: selected.data("repository"),
};
}
function getSelection() {
return table.bootstrapTable("getSelections").map(row => row.id);
}
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() {
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) {
keyImportButton.attr("hidden", hidden);
packageAddButton.attr("hidden", hidden);
@ -82,6 +68,24 @@
packageUpdateButton.attr("hidden", hidden);
}
function packagesRemove(packages) {
packages = packages ?? getSelection();
const onSuccess = update => `Packages ${update} have been removed`;
const onFailure = error => `Could not remove packages: ${error}`;
doPackageAction("/api/v1/service/remove", packages, repository, onSuccess, onFailure);
}
function packagesUpdate() {
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, repository, onSuccess, onFailure);
}
function reload() {
table.bootstrapTable("showLoading");
@ -95,33 +99,25 @@
$.ajax({
url: "/api/v1/packages",
data: {
architecture: repository.architecture,
repository: repository.repository,
},
type: "GET",
dataType: "json",
success: response => {
const extractListProperties = (description, property) => {
return Object.values(description.packages)
.map(pkg => pkg[property])
.reduce((left, right) => left.concat(right), []);
};
const listToTable = data => {
return Array.from(new Set(data))
.sort()
.map(entry => safe(entry))
.join("<br>");
};
const payload = response.map(description => {
const package_base = description.package.base;
const web_url = description.package.remote.web_url;
return {
id: package_base,
base: web_url ? `<a href="${safe(web_url)}" title="${safe(package_base)}">${safe(package_base)}</a>` : safe(package_base),
base: web_url ? safeLink(web_url, package_base, package_base).outerHTML : safe(package_base),
version: safe(description.package.version),
packager: description.package.packager ? safe(description.package.packager) : "",
packages: listToTable(Object.keys(description.package.packages)),
groups: listToTable(extractListProperties(description.package, "groups")),
licenses: listToTable(extractListProperties(description.package, "licenses")),
timestamp: new Date(1000 * description.status.timestamp).toISOString(),
timestamp: new Date(1000 * description.status.timestamp).toISOStringShort(),
status: description.status.status,
};
});
@ -149,15 +145,18 @@
$.ajax({
url: "/api/v1/status",
data: {
architecture: repository.architecture,
repository: repository.repository,
},
type: "GET",
dataType: "json",
success: response => {
repositoryBadge.text(`${response.repository} ${response.architecture}`);
versionBadge.html(`<i class="bi bi-github"></i> ahriman ${safe(response.version)}`);
statusBadge
.popover("dispose")
.attr("data-bs-content", `${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`)
.attr("data-bs-content", `${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOStringShort()}`)
.popover();
statusBadge.removeClass();
statusBadge.addClass("btn");
@ -166,6 +165,12 @@
});
}
function selectRepository() {
const fragment = window.location.hash.replace("#", "") || "{{ repositories[0].id }}";
const element = $(`#${fragment}-link`);
element.click();
}
function statusFormat(value) {
const cellClass = status => {
if (status === "pending") return "table-warning";
@ -177,21 +182,48 @@
return {classes: cellClass(value)};
}
function filterListGroups() {
return extractDataList(table.bootstrapTable("getData"), "groups");
}
$(_ => {
$("#repositories a").on("click", event => {
const element = event.target;
repository = {
architecture: element.dataset.architecture,
repository: element.dataset.repository,
};
packageUpdateButton.html(`<i class="bi bi-play"></i> update<span class="d-none d-sm-inline"> ${safe(repository.repository)} (${safe(repository.architecture)})</span>`);
$(`#${element.id}`).tab("show");
reload();
});
function filterListLicenses() {
return extractDataList(table.bootstrapTable("getData"), "licenses");
}
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 showPackageInfo(data.id);
});
table.on("created-controls.bs.table", _ => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
function filterListPackagers() {
return extractDataList(table.bootstrapTable("getData"), "packager");
}
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");
});
});
$(() => {
table.bootstrapTable({});
statusBadge.popover();
reload();
selectRepository();
});
</script>
</script>

View File

@ -5,8 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/static/favicon.ico">
{% include "utils/style.jinja2" %}
{% include "user-style.jinja2" ignore missing %}
</head>

View File

@ -30,7 +30,8 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
</div>
<div class="container">
<table id="packages" class="table table-striped table-hover"
<table id="packages"
data-classes="table table-hover"
data-export-options='{"fileName": "packages"}'
data-filter-control="true"
data-filter-control-visible="false"
@ -53,16 +54,16 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<thead class="table-primary">
<tr>
<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-align="right" 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>
<th data-sortable="true" data-align="right" data-field="archive_size">archive size</th>
<th data-sortable="true" data-align="right" data-field="installed_size">installed size</th>
<th data-sortable="true" data-align="right" data-field="timestamp" data-filter-control="input" data-filter-custom-search="filterDateRange" data-filter-control-placeholder="(any date)">build date</th>
</tr>
</thead>
@ -101,25 +102,6 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<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");
@ -140,6 +122,28 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
function filterListLicenses() {
return extractDataList(table.bootstrapTable("getData"), "licenses");
}
$(_ => {
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");
});
});
});
</script>
</body>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="256.000000pt" height="256.000000pt" viewBox="0 0 256.000000 256.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,256.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1019 2546 c-51 -18 -92 -49 -136 -103 -34 -41 -83 -73 -112 -73 -6
0 4 22 20 49 17 27 30 54 28 59 -7 17 -153 -102 -176 -143 -13 -23 -30 -40
-40 -40 -15 0 -18 9 -18 61 0 49 6 73 33 125 l33 64 -42 -40 c-50 -49 -82 -97
-98 -150 -31 -102 -43 -125 -77 -146 -19 -11 -48 -42 -65 -67 -17 -26 -62 -76
-100 -112 -69 -66 -69 -66 -69 -120 0 -30 5 -62 10 -70 5 -8 10 -10 10 -4 0 7
21 35 46 63 69 77 154 95 154 33 0 -42 -27 -126 -50 -155 l-21 -26 25 -7 c34
-8 63 10 86 56 27 53 74 105 127 142 42 29 44 32 38 68 -6 33 -3 40 22 60 25
20 36 22 96 17 83 -7 121 5 137 43 7 16 19 31 28 34 10 3 -9 3 -40 1 -65 -6
-84 8 -48 37 34 27 118 24 180 -7 57 -29 115 -32 150 -8 l24 17 -24 6 c-14 3
-27 14 -28 24 -4 17 1 18 46 13 58 -7 118 -34 147 -67 38 -44 107 -80 149 -80
36 1 38 2 21 12 -41 23 -115 108 -115 133 0 13 58 -27 94 -64 42 -43 86 -58
133 -46 28 7 28 7 6 18 -13 5 -23 19 -23 29 0 14 6 19 23 16 17 -2 32 -23 62
-85 48 -99 63 -115 123 -141 97 -41 101 -40 50 16 -54 60 -80 113 -76 149 l3
26 58 -47 c61 -50 87 -89 87 -131 0 -43 27 -93 59 -109 38 -20 37 -21 36 11 0
45 24 38 47 -14 19 -41 22 -63 20 -146 -2 -91 0 -103 25 -148 45 -83 54 -83
47 2 -6 74 11 155 32 153 20 -2 47 -101 48 -179 1 -65 -3 -88 -22 -122 -24
-46 -28 -98 -10 -138 14 -30 24 -32 31 -5 8 30 37 25 37 -6 0 -53 -23 -113
-62 -160 -52 -63 -58 -81 -58 -167 l0 -72 35 65 c40 75 98 135 103 107 4 -19
-37 -114 -69 -158 -11 -16 -49 -46 -85 -66 -64 -37 -94 -76 -94 -122 0 -20 1
-21 24 -6 19 13 25 13 35 2 17 -21 -15 -43 -73 -51 -85 -11 -126 -26 -164 -61
-35 -31 -80 -95 -67 -95 3 0 29 12 57 26 52 26 168 45 168 28 0 -12 -92 -91
-123 -105 -20 -9 -34 -8 -71 6 -52 20 -104 14 -130 -14 -17 -19 -17 -20 6 -23
12 -2 24 -11 26 -20 4 -22 -37 -23 -189 -7 -99 11 -104 10 -151 -12 -26 -13
-48 -27 -48 -30 0 -3 25 -16 55 -28 30 -13 55 -26 55 -31 0 -8 -73 -20 -119
-20 -44 0 -99 20 -130 46 -37 31 -87 46 -124 38 l-27 -6 22 -18 c16 -13 20
-22 12 -29 -18 -19 -125 42 -174 100 -45 53 -95 82 -165 95 l-40 7 52 -41 c96
-76 114 -128 31 -86 -105 52 -120 69 -147 172 -9 31 -21 48 -44 62 -41 25 -61
26 -49 2 5 -9 7 -23 5 -29 -10 -27 -32 -11 -57 39 -21 43 -26 66 -26 132 0 71
-4 87 -29 131 -32 55 -80 106 -67 71 21 -58 18 -206 -4 -206 -5 0 -30 43 -56
95 l-48 96 17 47 c21 57 21 89 2 122 -14 25 -14 25 -15 3 0 -14 -6 -23 -16
-23 -14 0 -16 8 -10 53 4 28 18 70 31 92 14 22 31 55 40 73 18 37 32 152 19
152 -5 0 -17 -21 -28 -46 -18 -45 -61 -104 -75 -104 -4 0 -4 32 0 72 6 55 16
83 40 122 24 37 34 66 37 108 6 75 -1 91 -22 51 -16 -32 -48 -44 -61 -23 -8
12 22 69 40 76 19 8 41 43 65 105 l21 52 -29 -29 c-16 -16 -48 -40 -71 -54
-23 -14 -56 -45 -72 -69 -28 -43 -29 -47 -29 -175 0 -186 -12 -220 -43 -122
-10 29 -15 62 -11 72 4 14 -2 11 -20 -9 -31 -33 -33 -65 -7 -115 21 -40 38
-112 27 -112 -16 0 -58 35 -71 59 l-14 26 -1 -37 c0 -24 13 -63 36 -108 39
-76 51 -155 24 -155 -8 0 -20 16 -27 35 l-11 35 -7 -29 c-11 -40 11 -127 43
-175 15 -22 56 -72 90 -111 70 -78 66 -86 -28 -51 -71 27 -60 7 30 -54 71 -48
106 -93 86 -112 -4 -4 -46 12 -93 37 -110 58 -109 58 -87 20 29 -49 70 -81
188 -145 114 -63 148 -94 126 -120 -13 -16 -52 -6 -82 22 -28 25 -21 0 11 -44
45 -63 100 -83 259 -99 154 -15 221 -29 272 -54 l35 -18 -35 -6 c-56 -10 -164
-26 -175 -26 -5 -1 15 -10 45 -21 62 -23 124 -27 255 -15 72 6 94 5 109 -7 28
-21 11 -37 -42 -38 l-47 -1 40 -19 c63 -31 153 -22 244 25 80 41 166 66 224
66 l38 0 -26 -35 c-15 -19 -48 -48 -73 -66 l-47 -31 72 4 c71 4 72 4 130 59
66 64 104 86 160 95 27 4 42 2 50 -8 16 -19 4 -34 -43 -58 l-39 -19 54 -1 c71
0 129 28 181 88 22 26 68 80 102 120 35 41 74 89 87 108 13 19 26 33 28 31 2
-2 -3 -46 -12 -98 -20 -128 -20 -128 26 -73 52 63 64 91 83 196 18 108 39 150
69 146 23 -3 27 -20 14 -70 -11 -42 18 -25 49 28 34 58 41 139 20 222 -21 84
-21 83 19 172 19 43 38 99 41 125 l7 46 38 -68 39 -68 0 88 c0 80 -3 93 -35
157 -41 80 -47 140 -15 140 11 0 26 -12 34 -27 15 -27 15 -27 16 35 0 54 -4
70 -29 105 -16 23 -49 57 -74 76 -56 42 -111 153 -122 248 l-7 63 58 -26 c32
-14 71 -35 86 -45 40 -29 35 -10 -18 60 -34 45 -65 72 -117 103 -118 69 -153
102 -141 132 9 22 25 20 68 -9 42 -28 43 -26 14 26 -36 65 -136 119 -217 119
-48 0 -233 88 -211 100 8 4 44 11 80 14 129 14 130 15 58 47 -62 29 -64 29
-198 23 -121 -6 -138 -5 -153 11 -25 24 -6 43 46 47 l42 3 -35 20 c-25 13 -53
19 -98 19 -54 0 -74 -6 -143 -42 -76 -39 -139 -54 -139 -33 0 6 45 32 100 60
l99 50 -55 7 c-76 9 -111 2 -214 -46 -99 -46 -155 -51 -155 -15 0 14 12 24 44
36 40 14 42 16 20 22 -37 10 -80 8 -120 -7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,4 +1,5 @@
{#simplified version of full report#}
<b>{{ repository }} update</b>
{% for package in packages %}
<a href="{{ link_path }}/{{ package.filename }}">{{ package.name }}</a> {{ package.version }}{% endfor %}
<a href="{{ link_path }}/{{ package.filename }}">{{ package.name }}</a> {{ package.version }}
{% endfor %}

View File

@ -1,21 +1,21 @@
<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://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/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://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" crossorigin="anonymous" type="application/javascript"></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/tableexport.jquery.plugin@1.28.0/tableExport.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" integrity="sha384-IazMVNyYoUNx6357fWJoqtHYUWWCNHIXxFVtbpVgvImQNWuRP2WbHPaIb3QF8j97" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" 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://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/bootstrap-table.min.js" crossorigin="anonymous" type="application/javascript"></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.22.1/dist/extensions/export/bootstrap-table-export.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/resizable/bootstrap-table-resizable.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/filter-control/bootstrap-table-filter-control.js" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.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 src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous" type="application/javascript"></script>
<script>
async function copyToClipboard(text, button) {
@ -36,19 +36,17 @@
}, 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 extractListProperties(description, property) {
return Object.values(description.packages)
.map(pkg => pkg[property])
.reduce((left, right) => left.concat(right), []);
}
function filterContains(text, value) {
return value.includes(text.toLowerCase().trim());
}
@ -59,7 +57,7 @@
const [minDate, maxDate] = text.split(" - ");
const buildDate = asOfStartOfDay(new Date(value));
return (buildDate >= asOfStartOfDay(new Date(minDate))) && (buildDate <= asOfStartOfDay(new Date(maxDate)));
return (buildDate >= new Date(minDate)) && (buildDate <= new Date(maxDate));
}
function filterList(index, value, field, data) {
@ -67,4 +65,33 @@
// the library removes all symbols from string, so it is just string
return value.includes(dataList[index].toLowerCase());
}
function listToTable(data) {
return Array.from(new Set(data))
.sort()
.map(entry => safe(entry))
.join("<br>");
}
function safe(string) {
return String(string)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
// because I'm tired of safe element generation
function safeLink(url, text, title) {
const element = document.createElement("a");
element.href = url;
element.innerText = text;
if (title) element.title = title;
return element;
}
Date.prototype.toISOStringShort = function() {
const pad = number => String(number).padStart(2, "0");
return `${this.getFullYear()}-${pad(this.getMonth() + 1)}-${pad(this.getDate())} ${pad(this.getHours())}:${pad(this.getMinutes())}:${pad(this.getSeconds())}`;
}
</script>

View File

@ -1,15 +1,17 @@
<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://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.21.4/dist/bootstrap-table.min.css" integrity="sha384-pTEAhytv7JmEG2D7qiW5gY0lI5EKZ9n3CNmj6Qp+U3qhnmH2qnnN9KJbVwbtMHN0" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/bootstrap-table.min.css" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" integrity="sha384-1sLxvR8mXzjhvFY9f8mzXl97DNLepeZ0PnRiMMdm/rQsKjsrPZPJxYle2wwT2PMg" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.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/bootstrap-table@1.22.1/dist/extensions/filter-control/bootstrap-table-filter-control.css" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.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/bootswatch@5.3.2/dist/cosmo/bootstrap.min.css" 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">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css" crossorigin="anonymous" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css" crossorigin="anonymous" type="text/css">
<style>
.pre-scrollable {

View File

@ -1,18 +1,20 @@
# 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' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '--wait-timeout' '-V' '--version')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '-V' '--version' '--wait-timeout')
_shtab_ahriman_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_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' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username')
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_changes_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_package_changes_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_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')
@ -25,12 +27,12 @@ _shtab_ahriman_patch_list_option_strings=('-h' '--help' '-e' '--exit-code' '-v'
_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_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_create_keyring_option_strings=('-h' '--help')
_shtab_ahriman_repo_create_mirrorlist_option_strings=('-h' '--help')
_shtab_ahriman_repo_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--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_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_repo_remove_unknown_option_strings=('-h' '--help' '--dry-run')
@ -45,19 +47,22 @@ _shtab_ahriman_repo_sync_option_strings=('-h' '--help')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_repo_tree_option_strings=('-h' '--help' '-p' '--partitions')
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_service_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_service_config_option_strings=('-h' '--help' '--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_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_repo_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_service_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_repo_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_service_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_service_repositories_option_strings=('-h' '--help' '--id-only' '--no-id-only')
_shtab_ahriman_service_run_option_strings=('-h' '--help')
_shtab_ahriman_run_option_strings=('-h' '--help')
_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_init_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_init_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
@ -73,7 +78,7 @@ _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' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman___log_handler_choices=('console' 'syslog' 'journald')
_shtab_ahriman_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')
@ -132,11 +137,11 @@ _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__h_nargs=0
_shtab_ahriman_help___help_nargs=0
_shtab_ahriman_help_updates__h_nargs=0
_shtab_ahriman_help_updates___help_nargs=0
_shtab_ahriman_help_updates__e_nargs=0
@ -184,6 +189,12 @@ _shtab_ahriman_package_update__n_nargs=0
_shtab_ahriman_package_update___now_nargs=0
_shtab_ahriman_package_update__y_nargs=0
_shtab_ahriman_package_update___refresh_nargs=0
_shtab_ahriman_package_changes__h_nargs=0
_shtab_ahriman_package_changes___help_nargs=0
_shtab_ahriman_package_changes__e_nargs=0
_shtab_ahriman_package_changes___exit_code_nargs=0
_shtab_ahriman_package_changes_remove__h_nargs=0
_shtab_ahriman_package_changes_remove___help_nargs=0
_shtab_ahriman_package_remove_pos_0_nargs=+
_shtab_ahriman_package_remove__h_nargs=0
_shtab_ahriman_package_remove___help_nargs=0
@ -230,6 +241,10 @@ _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___changes_nargs=0
_shtab_ahriman_repo_check___no_changes_nargs=0
_shtab_ahriman_repo_check___check_files_nargs=0
_shtab_ahriman_repo_check___no_check_files_nargs=0
_shtab_ahriman_repo_check__e_nargs=0
_shtab_ahriman_repo_check___exit_code_nargs=0
_shtab_ahriman_repo_check___vcs_nargs=0
@ -239,6 +254,10 @@ _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___changes_nargs=0
_shtab_ahriman_check___no_changes_nargs=0
_shtab_ahriman_check___check_files_nargs=0
_shtab_ahriman_check___no_check_files_nargs=0
_shtab_ahriman_check__e_nargs=0
_shtab_ahriman_check___exit_code_nargs=0
_shtab_ahriman_check___vcs_nargs=0
@ -253,12 +272,21 @@ _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___changes_nargs=0
_shtab_ahriman_repo_daemon___no_changes_nargs=0
_shtab_ahriman_repo_daemon___check_files_nargs=0
_shtab_ahriman_repo_daemon___no_check_files_nargs=0
_shtab_ahriman_repo_daemon___dependencies_nargs=0
_shtab_ahriman_repo_daemon___no_dependencies_nargs=0
_shtab_ahriman_repo_daemon___dry_run_nargs=0
_shtab_ahriman_repo_daemon___increment_nargs=0
_shtab_ahriman_repo_daemon___no_increment_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___partitions_nargs=0
_shtab_ahriman_repo_daemon___no_partitions_nargs=0
_shtab_ahriman_repo_daemon___vcs_nargs=0
_shtab_ahriman_repo_daemon___no_vcs_nargs=0
_shtab_ahriman_repo_daemon__y_nargs=0
@ -267,12 +295,21 @@ _shtab_ahriman_daemon__h_nargs=0
_shtab_ahriman_daemon___help_nargs=0
_shtab_ahriman_daemon___aur_nargs=0
_shtab_ahriman_daemon___no_aur_nargs=0
_shtab_ahriman_daemon___changes_nargs=0
_shtab_ahriman_daemon___no_changes_nargs=0
_shtab_ahriman_daemon___check_files_nargs=0
_shtab_ahriman_daemon___no_check_files_nargs=0
_shtab_ahriman_daemon___dependencies_nargs=0
_shtab_ahriman_daemon___no_dependencies_nargs=0
_shtab_ahriman_daemon___dry_run_nargs=0
_shtab_ahriman_daemon___increment_nargs=0
_shtab_ahriman_daemon___no_increment_nargs=0
_shtab_ahriman_daemon___local_nargs=0
_shtab_ahriman_daemon___no_local_nargs=0
_shtab_ahriman_daemon___manual_nargs=0
_shtab_ahriman_daemon___no_manual_nargs=0
_shtab_ahriman_daemon___partitions_nargs=0
_shtab_ahriman_daemon___no_partitions_nargs=0
_shtab_ahriman_daemon___vcs_nargs=0
_shtab_ahriman_daemon___no_vcs_nargs=0
_shtab_ahriman_daemon__y_nargs=0
@ -327,6 +364,10 @@ _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___changes_nargs=0
_shtab_ahriman_repo_update___no_changes_nargs=0
_shtab_ahriman_repo_update___check_files_nargs=0
_shtab_ahriman_repo_update___no_check_files_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
@ -347,6 +388,10 @@ _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___changes_nargs=0
_shtab_ahriman_update___no_changes_nargs=0
_shtab_ahriman_update___check_files_nargs=0
_shtab_ahriman_update___no_check_files_nargs=0
_shtab_ahriman_update___dependencies_nargs=0
_shtab_ahriman_update___no_dependencies_nargs=0
_shtab_ahriman_update___dry_run_nargs=0
@ -400,14 +445,20 @@ _shtab_ahriman_repo_clean___pacman_nargs=0
_shtab_ahriman_repo_clean___no_pacman_nargs=0
_shtab_ahriman_service_config__h_nargs=0
_shtab_ahriman_service_config___help_nargs=0
_shtab_ahriman_service_config___info_nargs=0
_shtab_ahriman_service_config___no_info_nargs=0
_shtab_ahriman_service_config___secure_nargs=0
_shtab_ahriman_service_config___no_secure_nargs=0
_shtab_ahriman_config__h_nargs=0
_shtab_ahriman_config___help_nargs=0
_shtab_ahriman_config___info_nargs=0
_shtab_ahriman_config___no_info_nargs=0
_shtab_ahriman_config___secure_nargs=0
_shtab_ahriman_config___no_secure_nargs=0
_shtab_ahriman_repo_config__h_nargs=0
_shtab_ahriman_repo_config___help_nargs=0
_shtab_ahriman_repo_config___info_nargs=0
_shtab_ahriman_repo_config___no_info_nargs=0
_shtab_ahriman_repo_config___secure_nargs=0
_shtab_ahriman_repo_config___no_secure_nargs=0
_shtab_ahriman_service_config_validate__h_nargs=0
@ -426,6 +477,16 @@ _shtab_ahriman_service_key_import__h_nargs=0
_shtab_ahriman_service_key_import___help_nargs=0
_shtab_ahriman_key_import__h_nargs=0
_shtab_ahriman_key_import___help_nargs=0
_shtab_ahriman_service_repositories__h_nargs=0
_shtab_ahriman_service_repositories___help_nargs=0
_shtab_ahriman_service_repositories___id_only_nargs=0
_shtab_ahriman_service_repositories___no_id_only_nargs=0
_shtab_ahriman_service_run_pos_0_nargs=+
_shtab_ahriman_service_run__h_nargs=0
_shtab_ahriman_service_run___help_nargs=0
_shtab_ahriman_run_pos_0_nargs=+
_shtab_ahriman_run__h_nargs=0
_shtab_ahriman_run___help_nargs=0
_shtab_ahriman_service_setup__h_nargs=0
_shtab_ahriman_service_setup___help_nargs=0
_shtab_ahriman_service_setup___generate_salt_nargs=0
@ -535,7 +596,7 @@ _set_new_action() {
current_action_nargs=1
fi
current_action_args_start_index=$(( $word_index + 1 ))
current_action_args_start_index=$(( $word_index + 1 - $pos_only ))
current_action_is_positional=$2
}
@ -549,10 +610,20 @@ _set_new_action() {
# ${!x} -> ${hello} -> "world"
_shtab_ahriman() {
local completing_word="${COMP_WORDS[COMP_CWORD]}"
local completed_positional_actions
local current_action
local current_action_args_start_index
local current_action_choices
local current_action_compgen
local current_action_is_positional
local current_action_nargs
local current_option_strings
local sub_parsers
COMPREPLY=()
local prefix=_shtab_ahriman
local word_index=0
local pos_only=0 # "--" delimeter not encountered yet
_set_parser_defaults
word_index=1
@ -561,26 +632,30 @@ _shtab_ahriman() {
while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}"
if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ $pos_only = 1 || " $this_word " != " -- " ]]; then
if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ " ${current_option_strings[@]} " == *" ${this_word} "* ]]; then
# 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_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
if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index - $pos_only >= \
$current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true
fi
else
pos_only=1 # "--" delimeter encountered
fi
let "word_index+=1"
@ -588,7 +663,7 @@ _shtab_ahriman() {
# Generate the completions
if [[ "${completing_word}" == -* ]]; then
if [[ $pos_only = 0 && "${completing_word}" == -* ]]; then
# optional argument started: use option strings
COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
else

View File

@ -1,16 +1,16 @@
.TH AHRIMAN "1" "2023\-09\-02" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2024\-09\-04" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS
.B ahriman
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [--wait-timeout WAIT_TIMEOUT] [-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,service-tree-migrate,user-add,user-list,user-remove,web} ...
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-changes,package-changes-remove,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ...
.SH DESCRIPTION
ArcH linux ReposItory MANager
.SH OPTIONS
.TP
\fB\-a\fR \fI\,ARCHITECTURE\/\fR, \fB\-\-architecture\fR \fI\,ARCHITECTURE\/\fR
target architectures. For several subcommands it can be used multiple times
filter by target architecture
.TP
\fB\-c\fR \fI\,CONFIGURATION\/\fR, \fB\-\-configuration\fR \fI\,CONFIGURATION\/\fR
@ -38,33 +38,33 @@ force enable or disable reporting to web service
.TP
\fB\-r\fR \fI\,REPOSITORY\/\fR, \fB\-\-repository\fR \fI\,REPOSITORY\/\fR
target repository. For several subcommands it can be used multiple times
filter by target repository
.TP
\fB\-\-unsafe\fR
allow to run ahriman as non\-ahriman user. Some actions might be unavailable
.TP
\fB\-V\fR, \fB\-\-version\fR
show program's version number and exit
.TP
\fB\-\-wait\-timeout\fR \fI\,WAIT_TIMEOUT\/\fR
wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of
zero value, the application will wait infinitely
.TP
\fB\-V\fR, \fB\-\-version\fR
show program's version number and exit
.SH
COMMAND
.TP
\fBahriman\fR \fI\,aur\-search\/\fR
search for package
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help\-commands\-unsafe\/\fR
list unsafe commands
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help\-updates\/\fR
check for service updates
.TP
@ -74,6 +74,12 @@ application version
\fBahriman\fR \fI\,package\-add\/\fR
add package
.TP
\fBahriman\fR \fI\,package\-changes\/\fR
get package changes
.TP
\fBahriman\fR \fI\,package\-changes\-remove\/\fR
remove package changes
.TP
\fBahriman\fR \fI\,package\-remove\/\fR
remove package
.TP
@ -155,6 +161,12 @@ validate system configuration
\fBahriman\fR \fI\,service\-key\-import\/\fR
import PGP key
.TP
\fBahriman\fR \fI\,service\-repositories\/\fR
show repositories
.TP
\fBahriman\fR \fI\,service\-run\/\fR
run multiple commands
.TP
\fBahriman\fR \fI\,service\-setup\/\fR
initial service configuration
.TP
@ -201,25 +213,25 @@ show additional package information
sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted
by name
.SH COMMAND \fI\,'ahriman help'\/\fR
usage: ahriman help [\-h] [command]
show help message for application or command and exit
.TP
\fBcommand\fR
show help message for specific command
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
usage: ahriman help\-commands\-unsafe [\-h] [command ...]
usage: ahriman help\-commands\-unsafe [\-h] [subcommand ...]
list unsafe commands as defined in default args
.TP
\fBcommand\fR
\fBsubcommand\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 help'\/\fR
usage: ahriman help [\-h] [subcommand]
show help message for application or command and exit
.TP
\fBsubcommand\fR
show help message for specific command
.SH COMMAND \fI\,'ahriman help\-updates'\/\fR
usage: ahriman help\-updates [\-h] [\-e]
@ -237,7 +249,7 @@ print application and its dependencies versions
.SH COMMAND \fI\,'ahriman package\-add'\/\fR
usage: ahriman package\-add [\-h] [\-\-dependencies | \-\-no\-dependencies] [\-e] [\-\-increment | \-\-no\-increment] [\-n] [\-y]
[\-s {auto,archive,aur,directory,local,remote,repository}] [\-u USERNAME]
[\-s {auto,archive,aur,directory,local,remote,repository}] [\-u USERNAME] [\-v VARIABLE]
package [package ...]
add existing or new package to the build queue
@ -275,6 +287,33 @@ explicitly specify the package source for this command
\fB\-u\fR \fI\,USERNAME\/\fR, \fB\-\-username\fR \fI\,USERNAME\/\fR
build as user
.TP
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
apply specified makepkg variables to the next build
.SH COMMAND \fI\,'ahriman package\-changes'\/\fR
usage: ahriman package\-changes [\-h] [\-e] package
retrieve package changes stored in database
.TP
\fBpackage\fR
package base
.SH OPTIONS \fI\,'ahriman package\-changes'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.SH COMMAND \fI\,'ahriman package\-changes\-remove'\/\fR
usage: ahriman package\-changes\-remove [\-h] package
remove the package changes stored remotely
.TP
\fBpackage\fR
package base
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
usage: ahriman package\-remove [\-h] package [package ...]
@ -352,7 +391,7 @@ 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
.SH COMMAND \fI\,'ahriman patch\-list'\/\fR
usage: ahriman patch\-list [\-h] [\-e] [\-v VARIABLE] [package]
usage: ahriman patch\-list [\-h] [\-e] [\-v VARIABLE] package
list available patches for the package
@ -408,7 +447,9 @@ backup repository settings and database
path of the output archive
.SH COMMAND \fI\,'ahriman repo\-check'\/\fR
usage: ahriman repo\-check [\-h] [\-e] [\-\-vcs | \-\-no\-vcs] [\-y] [package ...]
usage: ahriman repo\-check [\-h] [\-\-changes | \-\-no\-changes] [\-\-check\-files | \-\-no\-check\-files] [\-e] [\-\-vcs | \-\-no\-vcs]
[\-y]
[package ...]
check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
@ -417,6 +458,14 @@ check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
filter check by package base
.SH OPTIONS \fI\,'ahriman repo\-check'\/\fR
.TP
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
.TP
\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories)
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
@ -440,8 +489,10 @@ 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]
usage: ahriman repo\-daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes]
[\-\-check\-files | \-\-no\-check\-files] [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run]
[\-\-increment | \-\-no\-increment] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
[\-\-partitions | \-\-no\-partitions] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
start process which periodically will run update process
@ -454,10 +505,26 @@ interval between runs in seconds
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates
.TP
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
.TP
\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories)
.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
.TP
\fB\-\-increment\fR, \fB\-\-no\-increment\fR
increment package release (pkgrel) on duplicate
.TP
\fB\-\-local\fR, \fB\-\-no\-local\fR
enable or disable checking of local packages for updates
@ -466,6 +533,14 @@ enable or disable checking of local packages for updates
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
include or exclude manual updates
.TP
\fB\-\-partitions\fR, \fB\-\-no\-partitions\fR
instead of updating whole repository, split updates into chunks
.TP
\fB\-u\fR \fI\,USERNAME\/\fR, \fB\-\-username\fR \fI\,USERNAME\/\fR
build as user
.TP
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
fetch actual version of VCS packages
@ -584,9 +659,9 @@ run triggers on empty build result as configured by settings
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] [\-\-aur | \-\-no\-aur] [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-e]
[\-\-increment | \-\-no\-increment] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-u USERNAME]
[\-\-vcs | \-\-no\-vcs] [\-y]
usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes] [\-\-check\-files | \-\-no\-check\-files]
[\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-e] [\-\-increment | \-\-no\-increment]
[\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
[package ...]
check for packages updates and run build process if requested
@ -600,6 +675,14 @@ filter check by package base
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
enable or disable checking for AUR updates
.TP
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
.TP
\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories)
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies
@ -664,11 +747,23 @@ clear directory with built packages
clear directory with pacman local database cache
.SH COMMAND \fI\,'ahriman service\-config'\/\fR
usage: ahriman service\-config [\-h] [\-\-secure | \-\-no\-secure]
usage: ahriman service\-config [\-h] [\-\-info | \-\-no\-info] [\-\-secure | \-\-no\-secure] [section] [key]
dump configuration for the specified architecture
.TP
\fBsection\fR
filter settings by section
.TP
\fBkey\fR
filter settings by key
.SH OPTIONS \fI\,'ahriman service\-config'\/\fR
.TP
\fB\-\-info\fR, \fB\-\-no\-info\fR
show additional information, e.g. configuration files
.TP
\fB\-\-secure\fR, \fB\-\-no\-secure\fR
hide passwords and secrets from output
@ -697,6 +792,25 @@ PGP key to import from public server
\fB\-\-key\-server\fR \fI\,KEY_SERVER\/\fR
key server for key import
.SH COMMAND \fI\,'ahriman service\-repositories'\/\fR
usage: ahriman service\-repositories [\-h] [\-\-id\-only | \-\-no\-id\-only]
list all available repositories
.SH OPTIONS \fI\,'ahriman service\-repositories'\/\fR
.TP
\fB\-\-id\-only\fR, \fB\-\-no\-id\-only\fR
show machine readable identifier instead
.SH COMMAND \fI\,'ahriman service\-run'\/\fR
usage: ahriman service\-run [\-h] command [command ...]
run multiple commands on success run of the previous command
.TP
\fBcommand\fR
command to be run (quoted) without ``ahriman``
.SH COMMAND \fI\,'ahriman service\-setup'\/\fR
usage: ahriman service\-setup [\-h] [\-\-build\-as\-user BUILD_AS_USER] [\-\-from\-configuration FROM_CONFIGURATION]
[\-\-generate\-salt | \-\-no\-generate\-salt] [\-\-makeflags\-jobs | \-\-no\-makeflags\-jobs]

View File

@ -19,6 +19,8 @@ _shtab_ahriman_commands() {
"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-changes:retrieve package changes stored in database"
"package-changes-remove:remove the package changes stored remotely"
"package-remove:remove package from the repository"
"package-status:request status of the package"
"package-status-remove:remove the package from the status page"
@ -52,11 +54,14 @@ _shtab_ahriman_commands() {
"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"
"run:run multiple commands on success run of the previous command"
"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-repositories:list all available repositories"
"service-run:run multiple commands on success run of the previous command"
"service-setup:create initial service configuration, requires root"
"service-shell:drop into python shell"
"service-tree-migrate:migrate repository tree between versions"
@ -78,17 +83,17 @@ _shtab_ahriman_commands() {
_shtab_ahriman_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-a,--architecture}"[target architectures. For several subcommands it can be used multiple times (default\: None)]:architecture:"
{-a,--architecture}"[filter by target architecture (default\: None)]:architecture:"
{-c,--configuration}"[configuration path (default\: \/etc\/ahriman.ini)]:configuration:"
"--force[force run, remove file lock (default\: False)]"
{-l,--lock}"[lock file (default\: \/tmp\/ahriman.lock)]:lock:"
{-l,--lock}"[lock file (default\: ahriman.pid)]:lock:"
"--log-handler[explicit log handler specification. If none set, the handler will be guessed from environment (default\: None)]:log_handler:(console syslog journald)"
{-q,--quiet}"[force disable any logging (default\: False)]"
{--report,--no-report}"[force enable or disable reporting to web service (default\: True)]:report:"
"*"{-r,--repository}"[target repository. For several subcommands it can be used multiple times (default\: None)]:repository:"
{-r,--repository}"[filter by target repository (default\: None)]:repository:"
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable (default\: False)]"
"--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:"
"(- : *)"{-V,--version}"[show program\'s version number and exit]"
"--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:"
)
_shtab_ahriman_add_options=(
@ -98,8 +103,9 @@ _shtab_ahriman_add_options=(
{--increment,--no-increment}"[increment package release (pkgrel) version on duplicate (default\: True)]:increment:"
{-n,--now}"[run update function after (default\: False)]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
{-s,--source}"[explicitly specify the package source for this command (default\: PackageSource.Auto)]:source:(auto archive aur directory local remote repository)"
{-s,--source}"[explicitly specify the package source for this command (default\: auto)]:source:(auto archive aur directory local remote repository)"
{-u,--username}"[build as user (default\: None)]:username:"
"*"{-v,--variable}"[apply specified makepkg variables to the next build (default\: None)]:variable:"
"(*):package source (base name, path to local files, remote URL):"
)
@ -113,6 +119,8 @@ _shtab_ahriman_aur_search_options=(
_shtab_ahriman_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
@ -130,7 +138,10 @@ _shtab_ahriman_clean_options=(
_shtab_ahriman_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: True)]:secure:"
":filter settings by section (default\: None):"
":filter settings by key (default\: None):"
)
_shtab_ahriman_config_validate_options=(
@ -142,9 +153,15 @@ _shtab_ahriman_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) on duplicate (default\: True)]:increment:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
{--partitions,--no-partitions}"[instead of updating whole repository, split updates into chunks (default\: True)]:partitions:"
{-u,--username}"[build as user (default\: None)]:username:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
)
@ -197,11 +214,23 @@ _shtab_ahriman_package_add_options=(
{--increment,--no-increment}"[increment package release (pkgrel) version on duplicate (default\: True)]:increment:"
{-n,--now}"[run update function after (default\: False)]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
{-s,--source}"[explicitly specify the package source for this command (default\: PackageSource.Auto)]:source:(auto archive aur directory local remote repository)"
{-s,--source}"[explicitly specify the package source for this command (default\: auto)]:source:(auto archive aur directory local remote repository)"
{-u,--username}"[build as user (default\: None)]:username:"
"*"{-v,--variable}"[apply specified makepkg variables to the next build (default\: None)]:variable:"
"(*):package source (base name, path to local files, remote URL):"
)
_shtab_ahriman_package_changes_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
":package base:"
)
_shtab_ahriman_package_changes_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":package base:"
)
_shtab_ahriman_package_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:"
@ -223,7 +252,7 @@ _shtab_ahriman_package_status_remove_options=(
_shtab_ahriman_package_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status (default\: BuildStatusEnum.Success)]:status:(unknown pending building failed success)"
{-s,--status}"[new package build status (default\: success)]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):"
)
@ -234,8 +263,9 @@ _shtab_ahriman_package_update_options=(
{--increment,--no-increment}"[increment package release (pkgrel) version on duplicate (default\: True)]:increment:"
{-n,--now}"[run update function after (default\: False)]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
{-s,--source}"[explicitly specify the package source for this command (default\: PackageSource.Auto)]:source:(auto archive aur directory local remote repository)"
{-s,--source}"[explicitly specify the package source for this command (default\: auto)]:source:(auto archive aur directory local remote repository)"
{-u,--username}"[build as user (default\: None)]:username:"
"*"{-v,--variable}"[apply specified makepkg variables to the next build (default\: None)]:variable:"
"(*):package source (base name, path to local files, remote URL):"
)
@ -250,7 +280,7 @@ _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 (default\: False)]"
"*"{-v,--variable}"[if set, show only patches for specified PKGBUILD variables (default\: None)]:variable:"
":package base (default\: None):"
":package base:"
)
_shtab_ahriman_patch_remove_options=(
@ -293,6 +323,8 @@ _shtab_ahriman_repo_backup_options=(
_shtab_ahriman_repo_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
@ -310,7 +342,10 @@ _shtab_ahriman_repo_clean_options=(
_shtab_ahriman_repo_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: True)]:secure:"
":filter settings by section (default\: None):"
":filter settings by key (default\: None):"
)
_shtab_ahriman_repo_config_validate_options=(
@ -330,9 +365,15 @@ _shtab_ahriman_repo_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) on duplicate (default\: True)]:increment:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
{--partitions,--no-partitions}"[instead of updating whole repository, split updates into chunks (default\: True)]:partitions:"
{-u,--username}"[build as user (default\: None)]:username:"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
)
@ -402,7 +443,7 @@ _shtab_ahriman_repo_sign_options=(
_shtab_ahriman_repo_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new status (default\: BuildStatusEnum.Success)]:status:(unknown pending building failed success)"
{-s,--status}"[new status (default\: success)]:status:(unknown pending building failed success)"
)
_shtab_ahriman_repo_sync_options=(
@ -422,6 +463,8 @@ _shtab_ahriman_repo_triggers_options=(
_shtab_ahriman_repo_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@ -438,6 +481,11 @@ _shtab_ahriman_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_run_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):command to be run (quoted) without \`\`ahriman\`\`:"
)
_shtab_ahriman_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@ -457,7 +505,10 @@ _shtab_ahriman_service_clean_options=(
_shtab_ahriman_service_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:"
{--secure,--no-secure}"[hide passwords and secrets from output (default\: True)]:secure:"
":filter settings by section (default\: None):"
":filter settings by key (default\: None):"
)
_shtab_ahriman_service_config_validate_options=(
@ -471,6 +522,16 @@ _shtab_ahriman_service_key_import_options=(
":PGP key to import from public server:"
)
_shtab_ahriman_service_repositories_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--id-only,--no-id-only}"[show machine readable identifier instead (default\: False)]:id_only:"
)
_shtab_ahriman_service_run_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):command to be run (quoted) without \`\`ahriman\`\`:"
)
_shtab_ahriman_service_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
@ -533,7 +594,7 @@ _shtab_ahriman_status_options=(
_shtab_ahriman_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status (default\: BuildStatusEnum.Success)]:status:(unknown pending building failed success)"
{-s,--status}"[new package build status (default\: success)]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):"
)
@ -544,6 +605,8 @@ _shtab_ahriman_sync_options=(
_shtab_ahriman_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@ -561,7 +624,7 @@ _shtab_ahriman_user_add_options=(
"--key[optional PGP key used by this user. The private key must be imported (default\: None)]:key:"
"--packager[optional packager id used for build process in form of \`Name Surname \<mail\@example.com\>\` (default\: None)]:packager:"
{-p,--password}"[user password. Blank password will be treated as empty password, which is in particular must be used for OAuth2 authorization type. (default\: None)]:password:"
{-R,--role}"[user access level (default\: UserAccess.Read)]:role:(unauthorized read reporter full)"
{-R,--role}"[user access level (default\: read)]:role:(unauthorized read reporter full)"
":username for web service:"
)
@ -614,6 +677,8 @@ _shtab_ahriman() {
init) _arguments -C -s $_shtab_ahriman_init_options ;;
key-import) _arguments -C -s $_shtab_ahriman_key_import_options ;;
package-add) _arguments -C -s $_shtab_ahriman_package_add_options ;;
package-changes) _arguments -C -s $_shtab_ahriman_package_changes_options ;;
package-changes-remove) _arguments -C -s $_shtab_ahriman_package_changes_remove_options ;;
package-remove) _arguments -C -s $_shtab_ahriman_package_remove_options ;;
package-status) _arguments -C -s $_shtab_ahriman_package_status_options ;;
package-status-remove) _arguments -C -s $_shtab_ahriman_package_status_remove_options ;;
@ -647,11 +712,14 @@ _shtab_ahriman() {
repo-triggers) _arguments -C -s $_shtab_ahriman_repo_triggers_options ;;
repo-update) _arguments -C -s $_shtab_ahriman_repo_update_options ;;
report) _arguments -C -s $_shtab_ahriman_report_options ;;
run) _arguments -C -s $_shtab_ahriman_run_options ;;
search) _arguments -C -s $_shtab_ahriman_search_options ;;
service-clean) _arguments -C -s $_shtab_ahriman_service_clean_options ;;
service-config) _arguments -C -s $_shtab_ahriman_service_config_options ;;
service-config-validate) _arguments -C -s $_shtab_ahriman_service_config_validate_options ;;
service-key-import) _arguments -C -s $_shtab_ahriman_service_key_import_options ;;
service-repositories) _arguments -C -s $_shtab_ahriman_service_repositories_options ;;
service-run) _arguments -C -s $_shtab_ahriman_service_run_options ;;
service-setup) _arguments -C -s $_shtab_ahriman_service_setup_options ;;
service-shell) _arguments -C -s $_shtab_ahriman_service_shell_options ;;
service-tree-migrate) _arguments -C -s $_shtab_ahriman_service_tree_migrate_options ;;
@ -674,4 +742,12 @@ _shtab_ahriman() {
typeset -A opt_args
_shtab_ahriman "$@"
if [[ $zsh_eval_context[-1] == eval ]]; then
# eval/source/. command, register function for later
compdef _shtab_ahriman -N ahriman
else
# autoload from fpath, call function directly
_shtab_ahriman "$@"
fi

View File

@ -0,0 +1,212 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from astroid import nodes
from collections.abc import Iterable
from enum import StrEnum
from pylint.checkers import BaseRawFileChecker
from pylint.lint import PyLinter
from typing import Any
class MethodType(StrEnum):
"""
method type enumeration
Attributes:
Class(MethodType): (class attribute) class method
Delete(MethodType): (class attribute) destructor-like methods
Init(MethodType): (class attribute) initialization method
Magic(MethodType): (class attribute) other magical methods
New(MethodType): (class attribute) constructor method
Normal(MethodType): (class attribute) usual method
Property(MethodType): (class attribute) property method
Static(MethodType): (class attribute) static method
"""
Class = "classmethod"
Delete = "del"
Init = "init"
Magic = "magic"
New = "new"
Normal = "regular"
Property = "property"
Static = "staticmethod"
class DefinitionOrder(BaseRawFileChecker):
"""
check if methods are defined in recommended order
Attributes:
DECORATED_METHODS_ORDER(list[str]): (class attribute) predefined list of known function decorators
"""
DECORATED_METHODS_ORDER = {
"cached_property": MethodType.Property,
"classmethod": MethodType.Class,
"property": MethodType.Property,
"staticmethod": MethodType.Static,
}
msgs = {
"W6001": (
"Invalid method order %s, expected %s",
"methods-out-of-order",
"Methods are defined out of recommended order.",
),
}
name = "method-ordering"
options = (
(
"method-type-order",
{
"default": [
"init",
"new",
"del",
"property",
"classmethod",
"staticmethod",
"regular",
"magic",
],
"type": "csv",
"metavar": "<comma-separated types>",
"help": "Method types order to check.",
},
),
)
@staticmethod
def methods(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.FunctionDef]:
"""
extract function nodes from list of raw nodes
Args:
source(Iterable[Any]): all available nodes
start_lineno(int, optional): minimal allowed line number (Default value = 0)
Returns:
list[nodes.FunctionDef]: list of function nodes
"""
def is_defined_function(function: Any) -> bool:
return isinstance(function, nodes.FunctionDef) \
and function.lineno is not None \
and function.lineno >= start_lineno
return list(filter(is_defined_function, source))
@staticmethod
def resolve_type(function: nodes.FunctionDef) -> MethodType:
"""
resolve type of the function
Args:
function(nodes.FunctionDef): function definition
Returns:
MethodType: resolved function type
"""
# init methods
if function.name in ("__init__", "__post_init__"):
return MethodType.Init
if function.name in ("__new__",):
return MethodType.New
if function.name in ("__del__",):
return MethodType.Delete
# decorated methods
decorators = []
if function.decorators is not None:
decorators = [getattr(decorator, "name", None) for decorator in function.decorators.get_children()]
for decorator in decorators:
if decorator in DefinitionOrder.DECORATED_METHODS_ORDER:
return DefinitionOrder.DECORATED_METHODS_ORDER[decorator]
# magic methods
if function.name.startswith("__") and function.name.endswith("__"):
return MethodType.Magic
# normal method
return MethodType.Normal
def check_class(self, clazz: nodes.ClassDef) -> None:
"""
check class functions ordering
Args:
clazz(nodes.ClassDef): class definition
"""
methods = self.methods(clazz.values(), clazz.lineno)
self.check_functions(methods)
def check_functions(self, functions: list[nodes.FunctionDef]) -> None:
"""
check global functions ordering
Args:
functions(list[nodes.FunctionDef]): list of functions in their defined order
"""
for real, expected in zip(functions, sorted(functions, key=self.comparator)):
if real == expected:
continue
self.add_message("methods-out-of-order", line=real.lineno, args=(real.name, expected.name))
break
def comparator(self, function: nodes.FunctionDef) -> tuple[int, str]:
"""
compare key for sorting function
Args:
function(nodes.FunctionDef): function definition
Returns:
tuple[int, str]: comparison key for the specified function definition
"""
function_type = self.resolve_type(function)
try:
function_type_index = self.linter.config.method_type_order.index(function_type)
except ValueError:
function_type_index = len(self.linter.config.method_type_order) # not in the list
return function_type_index, function.name
def process_module(self, node: nodes.Module) -> None:
"""
process module
Args:
node(nodes.Module): module node to check
"""
# check global methods
self.check_functions(self.methods(node.values()))
# check class definitions
for clazz in filter(lambda method: isinstance(method, nodes.ClassDef), node.values()):
self.check_class(clazz)
def register(linter: PyLinter) -> None:
"""
register custom checker
Args:
linter(PyLinter): linter in which checker should be registered
"""
linter.register_checker(DefinitionOrder(linter))

View File

@ -0,0 +1,196 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from astroid import nodes
from collections.abc import Iterable
from enum import StrEnum
from pylint.checkers import BaseRawFileChecker
from pylint.lint import PyLinter
from typing import Any
class ImportType(StrEnum):
"""
import type enumeration
Attributes:
Package(MethodTypeOrder): (class attribute) package import
PackageFrom(MethodTypeOrder): (class attribute) package import, from clause
System(ImportType): (class attribute) system installed packages
SystemFrom(MethodTypeOrder): (class attribute) system installed packages, from clause
"""
Package = "package"
PackageFrom = "package-from"
System = "system"
SystemFrom = "system-from"
class ImportOrder(BaseRawFileChecker):
"""
check if imports are defined in recommended order
"""
msgs = {
"W6002": (
"Invalid import order %s, expected before %s",
"imports-out-of-order",
"Imports are defined out of recommended order.",
),
"W6003": (
"Import contains more than one package: %s",
"multiple-package-imports",
"Multiple package imports are not allowed.",
),
"W6004": (
"Invalid from import order %s, expected %s",
"from-imports-out-of-order",
"From imports are defined out of recommended order.",
),
}
name = "import-ordering"
options = (
(
"import-type-order",
{
"default": [
"system",
"system-from",
"package",
"package-from",
],
"type": "csv",
"metavar": "<comma-separated types>",
"help": "Import types order to check.",
},
),
(
"root-module",
{
"default": "ahriman",
"type": "string",
"help": "Root module name",
}
)
)
@staticmethod
def imports(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.Import | nodes.ImportFrom]:
"""
extract import nodes from list of raw nodes
Args:
source(Iterable[Any]): all available nodes
start_lineno(int, optional): minimal allowed line number (Default value = 0)
Returns:
list[nodes.Import | nodes.ImportFrom]: list of import nodes
"""
def is_defined_import(imports: Any) -> bool:
return isinstance(imports, (nodes.Import, nodes.ImportFrom)) \
and imports.lineno is not None \
and imports.lineno >= start_lineno
return list(filter(is_defined_import, source))
def check_from_imports(self, imports: nodes.ImportFrom) -> None:
"""
check import from statement
Args:
imports(nodes.ImportFrom): import from node
"""
imported = [names for names, _ in imports.names]
for real, expected in zip(imported, sorted(imported)):
if real == expected:
continue
self.add_message("from-imports-out-of-order", line=imports.lineno, args=(real, expected))
break
def check_imports(self, imports: list[nodes.Import | nodes.ImportFrom], root_package: str) -> None:
"""
check imports
Args:
imports(list[nodes.Import | nodes.ImportFrom]): list of imports in their defined order
root_package(str): root package name
"""
last_statement: tuple[int, str] | None = None
for statement in imports:
# define types and perform specific checks
if isinstance(statement, nodes.ImportFrom):
import_name = statement.modname
root, *_ = import_name.split(".", maxsplit=1)
import_type = ImportType.PackageFrom if root_package == root else ImportType.SystemFrom
# check from import itself
self.check_from_imports(statement)
else:
import_name = next(name for name, _ in statement.names)
root, *_ = import_name.split(".", maxsplit=1)[0]
import_type = ImportType.Package if root_package == root else ImportType.System
# check import itself
self.check_package_imports(statement)
# extract index
try:
import_type_index = self.linter.config.import_type_order.index(import_type)
except ValueError:
import_type_index = len(self.linter.config.import_type_order)
# check ordering if possible
if last_statement is not None:
_, last_statement_name = last_statement
if last_statement > (import_type_index, import_name):
self.add_message("imports-out-of-order", line=statement.lineno,
args=(import_name, last_statement_name))
# update the last value
last_statement = import_type_index, import_name
def check_package_imports(self, imports: nodes.Import) -> None:
"""
check package import
Args:
imports(nodes.Import): package import node
"""
if len(imports.names) != 1:
self.add_message("multiple-package-imports", line=imports.lineno, args=(imports.names,))
def process_module(self, node: nodes.Module) -> None:
"""
process module
Args:
node(nodes.Module): module node to check
"""
root_module, *_ = node.qname().split(".")
self.check_imports(self.imports(node.values()), root_module)
def register(linter: PyLinter) -> None:
"""
register custom checker
Args:
linter(PyLinter): linter in which checker should be registered
"""
linter.register_checker(ImportOrder(linter))

View File

@ -8,7 +8,8 @@ name = "ahriman"
description = "ArcH linux ReposItory MANager"
readme = "README.md"
requires-python = ">=3.11"
# Actually we are using features from the latest python, however, ubuntu, which is used for CI doesn't have it
requires-python = ">=3"
license = {file = "COPYING"}
authors = [
@ -19,6 +20,7 @@ dependencies = [
"cerberus",
"inflection",
"passlib",
"pyelftools",
"requests",
"srcinfo",
]
@ -40,7 +42,7 @@ check = [
"mypy",
"pylint",
]
docs = [
docs = [
"Sphinx",
"argparse-manpage",
"pydeps",
@ -76,9 +78,21 @@ web = [
"aiohttp-apispec",
"aiohttp_cors",
"aiohttp_jinja2",
"aiohttp_debugtoolbar",
"aiohttp_session",
"aiohttp_security",
"cryptography",
"requests-unixsocket", # required by unix socket support
"requests-unixsocket2", # required by unix socket support
"setuptools", # required by aiohttp-apispec
]
[tool.flit.sdist]
include = [
"AUTHORS",
"CONTRIBUTING.md",
"SECURITY.md",
"package",
"web.png",
]
exclude = [
"package/archlinux",
]

18
recipes/README.md Normal file
View File

@ -0,0 +1,18 @@
# Recipes
Collection of the examples of docker compose configuration files, which covers some specific cases. Not for production use.
## Configurations
* [Check](check): double process service; one with periodic checks (automatic build disabled) and other one is with the web service.
* [Daemon](daemon): service with periodic repository checks.
* [Distributed](distributed): cluster of three nodes, one with web interface and two workers which are responsible for build process.
* [Distributed manual](distributed-manual): same as [distributed](distributed), but two nodes and update process must be run on worker node manually.
* [i686](i686): non-x86_64 architecture setup.
* [Index](index): repository with index page generator enabled.
* [Multi repo](multirepo): run web service with two separated repositories.
* [OAuth](oauth): web service with OAuth (GitHub provider) authentication enabled.
* [PAM](pam): web service with PAM authentication enabled.
* [Pull](pull): normal service, but in addition with pulling packages from another source (e.g. GitHub repository).
* [Sign](sign): create repository with database signing.
* [Web](web): simple web service with authentication enabled.

7
recipes/check/README.md Normal file
View File

@ -0,0 +1,7 @@
# Check
1. Create user `demo` with password from `AHRIMAN_PASSWORD` environment variable.
2. Setup repository named `ahriman-demo` with architecture `x86_64`.
3. Start web server at port `8080`.
4. Start periodic updates check as separated container without building.
5. Repository is available at `http://localhost:8080/repo`.

86
recipes/check/compose.yml Normal file
View File

@ -0,0 +1,86 @@
services:
backend:
image: arcan1s/ahriman:edge
privileged: true
environment:
AHRIMAN_DEBUG: yes
AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
configs:
- source: service
target: /etc/ahriman.ini.d/99-settings.ini
secrets:
- password
volumes:
- type: volume
source: repository
target: /var/lib/ahriman
volume:
nocopy: true
healthcheck:
test: curl --fail --silent --output /dev/null http://backend:8080/api/v1/info
interval: 10s
start_period: 30s
command: web
frontend:
image: nginx
ports:
- 8080:80
configs:
- source: nginx
target: /etc/nginx/conf.d/default.conf
volumes:
- type: volume
source: repository
target: /srv
read_only: true
volume:
nocopy: true
worker:
image: arcan1s/ahriman:edge
privileged: true
environment:
AHRIMAN_DEBUG: yes
AHRIMAN_OUTPUT: console
AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
volumes:
- type: volume
source: repository
target: /var/lib/ahriman
volume:
nocopy: true
depends_on:
backend:
condition: service_healthy
command: repo-daemon --dry-run
configs:
nginx:
file: nginx.conf
service:
file: service.ini
secrets:
password:
environment: AHRIMAN_PASSWORD
volumes:
repository:

18
recipes/check/nginx.conf Normal file
View File

@ -0,0 +1,18 @@
server {
listen 80;
location /repo {
rewrite ^/repo/(.*) /$1 break;
autoindex on;
root /srv/ahriman/repository;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
proxy_pass http://backend:8080;
}
}

View File

@ -0,0 +1,2 @@
[auth]
target = configuration

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