mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-27 14:22:10 +00:00
Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
b2ed383de0 | |||
551ee670bf | |||
5d4bd9e459 | |||
4f21eb6fe6 | |||
9a008ddafa | |||
0cd07afa0f | |||
f590136197 | |||
1855e513a8 | |||
233dda8420 | |||
9556779ee2 | |||
8b32bd43db | |||
0eadef597a | |||
e58ccdc8ad | |||
2a07356d24 | |||
43c553a3db | |||
0d1a890bd0 | |||
3aeaa8ce2c | |||
7cee859f19 | |||
fb6b22cdd7 | |||
4f35ec6542 | |||
d98211e5e5 | |||
b97c8928e1 | |||
649df81aa5 | |||
ad7cdb7d95 | |||
4bb598d2eb | |||
f47be6cab0 | |||
342b3cb652 | |||
fc0d8387df | |||
e0b0c3caeb | |||
61969dd682 | |||
e441d93a56 | |||
664b6369bb | |||
4f6bd29ff4 | |||
f6d9ea480a | |||
08f62842ba | |||
1912790ccc | |||
cf3c48ffeb | |||
6633766cc3 | |||
f73d1eb424 | |||
87ab3683ad | |||
9c2f73af8c | |||
31f551bdf2 | |||
cbbe2b01e4 | |||
cd361a483d | |||
cee4fd4cce | |||
18daecaac7 | |||
03c298c762 | |||
79d4a488a0 | |||
22847aac68 | |||
75b9a96798 | |||
a0cfb7561d | |||
f4131b8cd7 | |||
9f134e37b6 | |||
07b7689701 | |||
47de715d7d | |||
5674b7b388 | |||
f6e57a411c | |||
38e48b1571 | |||
d492c968ea | |||
a11fd188a2 | |||
2431d5de0e |
@ -1 +1 @@
|
||||
skips: ['B101', 'B105', 'B404']
|
||||
skips: ['B101', 'B105', 'B106', 'B404']
|
14
.dockerignore
Normal file
14
.dockerignore
Normal file
@ -0,0 +1,14 @@
|
||||
.eggs/
|
||||
.git/
|
||||
.github/
|
||||
.idea/
|
||||
.mypy_cache/
|
||||
.pytest_cache/
|
||||
.tox/
|
||||
.venv/
|
||||
|
||||
*.egg-info/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyd
|
||||
*.pyo
|
10
.github/ISSUE_TEMPLATE/bug-report.md
vendored
10
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -11,9 +11,9 @@ assignees: ''
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### Steps to Reproduce
|
||||
### Steps to reproduce
|
||||
|
||||
Steps to reproduce the behavior (commands, environment etc)
|
||||
Steps to reproduce the behavior (commands, environment etc).
|
||||
|
||||
### Expected behavior
|
||||
|
||||
@ -21,4 +21,8 @@ A clear and concise description of what you expected to happen.
|
||||
|
||||
### Logs
|
||||
|
||||
Add logs to help explain your problem. Logs to stderr can be generated by using `--no-log` command line option.
|
||||
Add logs to help explain your problem. By default, the application writes logs into `/dev/log` which is usually default systemd journal and can be accessed by `journalctl` command.
|
||||
|
||||
You can also attach any additional information which can be helpful, e.g. configuration used by the application (be aware of passwords and other secrets if any); it can be generated by using `ahriman config` command.
|
||||
|
||||
It is also sometimes useful to have information about installed packages which can be accessed by `ahriman version` command.
|
||||
|
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@ -13,7 +13,7 @@ Brief description of the feature required
|
||||
|
||||
### Cause of the feature request
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
A clear and concise description of what the problem is. E.g. I'm always frustrated when [...]
|
||||
|
||||
### Proposed changes and/or features
|
||||
|
||||
|
16
.github/workflows/run-setup.yml
vendored
16
.github/workflows/run-setup.yml
vendored
@ -7,6 +7,22 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
run-setup-minimal:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: archlinux:latest
|
||||
volumes:
|
||||
- ${{ github.workspace }}:/build
|
||||
options: --privileged -w /build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: setup the minimal service in arch linux container
|
||||
run: .github/workflows/setup.sh minimal
|
||||
|
||||
run-setup:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
33
.github/workflows/setup.sh
vendored
33
.github/workflows/setup.sh
vendored
@ -3,21 +3,25 @@
|
||||
|
||||
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
|
||||
# refresh the image
|
||||
pacman --noconfirm -Syu
|
||||
# main dependencies
|
||||
pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-srcinfo sudo
|
||||
pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-setuptools python-srcinfo sudo
|
||||
# make dependencies
|
||||
pacman --noconfirm -Sy python-build python-installer python-wheel
|
||||
# optional dependencies
|
||||
# VCS support
|
||||
pacman --noconfirm -Sy breezy darcs mercurial subversion
|
||||
# web server
|
||||
pacman --noconfirm -Sy python-aioauth-client python-aiohttp python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
|
||||
# additional features
|
||||
pacman --noconfirm -Sy gnupg python-boto3 rsync
|
||||
if [[ -z $MINIMAL_INSTALL ]]; then
|
||||
# VCS support
|
||||
pacman --noconfirm -Sy breezy darcs mercurial subversion
|
||||
# web server
|
||||
pacman --noconfirm -Sy python-aioauth-client python-aiohttp python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
|
||||
# additional features
|
||||
pacman --noconfirm -Sy gnupg python-boto3 rsync
|
||||
fi
|
||||
|
||||
# create fresh tarball
|
||||
make VERSION=1.0.0 archlinux # well, it does not really matter which version we will put here
|
||||
@ -33,14 +37,17 @@ systemd-machine-id-setup
|
||||
# special thing for the container, because /dev/log interface is not available there
|
||||
sed -i "s/handlers = syslog_handler/handlers = console_handler/g" /etc/ahriman.ini.d/logging.ini
|
||||
# initial setup command as root
|
||||
ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "github" --web-port 8080
|
||||
[[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080")
|
||||
ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "github" "${WEB_ARGS[@]}"
|
||||
# enable services
|
||||
systemctl enable ahriman-web@x86_64
|
||||
systemctl enable ahriman@x86_64.timer
|
||||
# run web service (detached)
|
||||
sudo -u ahriman -- ahriman -a x86_64 web &
|
||||
WEBPID=$!
|
||||
sleep 15s # wait for the web service activation
|
||||
if [[ -z $MINIMAL_INSTALL ]]; then
|
||||
# run web service (detached)
|
||||
sudo -u ahriman -- ahriman -a x86_64 web &
|
||||
WEB_PID=$!
|
||||
sleep 15s # wait for the web service activation
|
||||
fi
|
||||
# add the first package
|
||||
# the build itself does not really work in the container
|
||||
sudo -u ahriman -- ahriman package-add --now yay
|
||||
@ -49,4 +56,4 @@ sudo -u ahriman -- ahriman package-add --now yay
|
||||
# run package check
|
||||
sudo -u ahriman -- ahriman repo-update
|
||||
# stop web service lol
|
||||
kill $WEBPID
|
||||
[[ -z $WEB_PID ]] || kill $WEB_PID
|
||||
|
2
.github/workflows/tests.sh
vendored
2
.github/workflows/tests.sh
vendored
@ -4,7 +4,7 @@
|
||||
set -ex
|
||||
|
||||
# install dependencies
|
||||
pacman --noconfirm -Syu base-devel python-pip python-tox
|
||||
pacman --noconfirm -Syu base-devel python-pip python-setuptools python-tox
|
||||
|
||||
# run test and check targets
|
||||
make check tests
|
||||
|
78
.pylintrc
78
.pylintrc
@ -60,17 +60,7 @@ confidence=
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=print-statement,
|
||||
parameter-unpacking,
|
||||
unpacking-in-except,
|
||||
old-raise-syntax,
|
||||
backtick,
|
||||
long-suffix,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
import-star-module-level,
|
||||
non-ascii-bytes-literal,
|
||||
raw-checker-failed,
|
||||
disable=raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
@ -78,67 +68,6 @@ disable=print-statement,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
apply-builtin,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
cmp-builtin,
|
||||
coerce-builtin,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
long-builtin,
|
||||
raw_input-builtin,
|
||||
reduce-builtin,
|
||||
standarderror-builtin,
|
||||
unicode-builtin,
|
||||
xrange-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
getslice-method,
|
||||
setslice-method,
|
||||
no-absolute-import,
|
||||
old-division,
|
||||
dict-iter-method,
|
||||
dict-view-method,
|
||||
next-method-called,
|
||||
metaclass-assignment,
|
||||
indexing-exception,
|
||||
raising-string,
|
||||
reload-builtin,
|
||||
oct-method,
|
||||
hex-method,
|
||||
nonzero-method,
|
||||
cmp-method,
|
||||
input-builtin,
|
||||
round-builtin,
|
||||
intern-builtin,
|
||||
unichr-builtin,
|
||||
map-builtin-not-iterating,
|
||||
zip-builtin-not-iterating,
|
||||
range-builtin-not-iterating,
|
||||
filter-builtin-not-iterating,
|
||||
using-cmp-argument,
|
||||
eq-without-hash,
|
||||
div-method,
|
||||
idiv-method,
|
||||
rdiv-method,
|
||||
exception-message-attribute,
|
||||
invalid-str-codec,
|
||||
sys-max-int,
|
||||
bad-python3-import,
|
||||
deprecated-string-function,
|
||||
deprecated-str-translate-call,
|
||||
deprecated-itertools-function,
|
||||
deprecated-types-field,
|
||||
next-method-defined,
|
||||
dict-items-not-iterating,
|
||||
dict-keys-not-iterating,
|
||||
dict-values-not-iterating,
|
||||
deprecated-operator-function,
|
||||
deprecated-urllib-function,
|
||||
xreadlines-attribute,
|
||||
deprecated-sys-function,
|
||||
exception-escape,
|
||||
comprehension-escape,
|
||||
missing-module-docstring,
|
||||
line-too-long,
|
||||
no-name-in-module,
|
||||
@ -153,7 +82,8 @@ disable=print-statement,
|
||||
fixme,
|
||||
too-many-arguments,
|
||||
duplicate-code,
|
||||
cyclic-import
|
||||
cyclic-import,
|
||||
confusing-with-statement,
|
||||
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
@ -219,7 +149,7 @@ indent-string=' '
|
||||
max-line-length=100
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
max-module-lines=400
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
|
@ -5,7 +5,7 @@ formats: all
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
python: "3.10"
|
||||
|
||||
sphinx:
|
||||
builder: html
|
||||
|
68
CONTRIBUTING.md
Normal file
68
CONTRIBUTING.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Contributing to ahriman
|
||||
|
||||
Welcome to ahriman! The goal of the project is to provide the best user experience to manage Arch linux repositories. In order to follow this we set some limitations for the issue creations and heavily restricted code contribution.
|
||||
|
||||
## Create an issue
|
||||
|
||||
Basically just follow the suggested templates:
|
||||
|
||||
* Bug report requires at least the way to reproduce the issue and behaviour description (expected and actual ones). In order to resolve the bug, the additional questions may be asked, please consider them as lesser evil.
|
||||
* Feature requests basically just require feature description and the purpose why do you want this feature to be implemented. It is required to make sure that the feature you want is going to be implemented in the way you really want it (and to make sure that this feature is not already implemented).
|
||||
* Questions and discussions have free templates, and you are free to ask your question in the way you want.
|
||||
|
||||
## Code contribution
|
||||
|
||||
There are some strict limitation for suggested pull requests:
|
||||
|
||||
* `autopep8`, `bandit`, `pylint`, `mypy` must pass.
|
||||
* Test coverage must remain 100%.
|
||||
|
||||
### 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.
|
||||
|
||||
### Code style
|
||||
|
||||
Again, the most checks can be performed by `make check` command, though some additional guidelines must be applied:
|
||||
|
||||
* Every class, every function (including private and protected), every attribute must be documented. The project follows [Google style documentation](https://google.github.io/styleguide/pyguide.html). The only exception is local functions.
|
||||
* Type annotations are the must, even for local functions.
|
||||
* 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).
|
||||
* For the external command `ahriman.core.util.check_output` function must be used.
|
||||
* Every temporary file/directory must be removed at the end of processing, no matter what. The `tempfile` module provides good ways to do it.
|
||||
* Import order must be the following:
|
||||
|
||||
```python
|
||||
# optional imports from future module
|
||||
from __future__ import annotations
|
||||
|
||||
# Module import for those which are installed into environment (no matter standard library or not)...
|
||||
import os
|
||||
# ...one per line...
|
||||
import pyalpm
|
||||
# ...in alphabetical order
|
||||
import sys
|
||||
|
||||
# Blank line between
|
||||
# ``from module import submodule`` import
|
||||
from pathlib import Path
|
||||
# ...again in alphabet order. It is possible to do several imports, but make sure that they are also in alphabetical order.
|
||||
from pyalpm import Handle, Package
|
||||
|
||||
# Blank line again and package imports
|
||||
from ahriman.core.configuration import Configuration
|
||||
```
|
||||
|
||||
* One file should define only one class, exception is class satellites in case if file length remains less than 400 lines.
|
||||
* It is possible to create file which contains some functions (e.g. `ahriman.core.util`), but in this case you would need to define `__all__` attribute.
|
||||
* The file size mentioned above must be applicable in general. In case of big classes consider splitting them into traits. Note, however, that `pylint` includes comments and docstrings into counter, thus you need to check file size by other tools.
|
||||
* No global variable is allowed outside of `ahriman.version` module.
|
||||
* Single quotes are not allowed. The reason behind this restriction is the fact that docstrings must be written by using double quotes only, and we would like to make style consistent.
|
||||
* If your class writes anything to log, the `ahriman.core.lazy_logging.LazyLogging` trait must be used.
|
||||
|
||||
### 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.
|
37
Dockerfile
37
Dockerfile
@ -1,4 +1,4 @@
|
||||
FROM archlinux:base-devel
|
||||
FROM archlinux:base
|
||||
|
||||
# image configuration
|
||||
ENV AHRIMAN_ARCHITECTURE="x86_64"
|
||||
@ -13,24 +13,25 @@ ENV AHRIMAN_REPOSITORY_ROOT="/var/lib/ahriman/ahriman"
|
||||
ENV AHRIMAN_USER="ahriman"
|
||||
|
||||
# install environment
|
||||
## install git which is required for AUR interaction and go for yay
|
||||
RUN pacman --noconfirm -Syu git go
|
||||
## 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
|
||||
## create build user
|
||||
RUN useradd -m -d /home/build -s /usr/bin/nologin build && \
|
||||
echo "build ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/build
|
||||
## install AUR helper
|
||||
RUN YAY_DIR="$(runuser -u build -- mktemp -d)" && \
|
||||
git clone https://aur.archlinux.org/yay.git "$YAY_DIR" && \
|
||||
cd "$YAY_DIR" && \
|
||||
runuser -u build -- makepkg --noconfirm --install && \
|
||||
cd - && rm -r "$YAY_DIR"
|
||||
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
|
||||
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
|
||||
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
|
||||
## install package dependencies
|
||||
RUN runuser -u build -- yay --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-srcinfo && \
|
||||
runuser -u build -- yay --noconfirm -Sy python-build python-installer python-wheel && \
|
||||
runuser -u build -- yay --noconfirm -Sy breezy darcs mercurial python-aioauth-client python-aiohttp \
|
||||
python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security \
|
||||
python-aiohttp-session python-boto3 python-cryptography python-jinja \
|
||||
rsync subversion
|
||||
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
|
||||
RUN pacman --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-setuptools python-srcinfo && \
|
||||
pacman --noconfirm -Sy python-build python-installer python-wheel && \
|
||||
pacman --noconfirm -Sy breezy mercurial python-aiohttp python-boto3 python-cryptography python-jinja rsync subversion && \
|
||||
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-jinja2 python-aiohttp-debugtoolbar \
|
||||
python-aiohttp-session python-aiohttp-security
|
||||
|
||||
# cleanup unused
|
||||
RUN find "/var/cache/pacman/pkg" -type f -delete
|
||||
|
||||
# install ahriman
|
||||
## copy tree
|
||||
@ -41,7 +42,7 @@ RUN cd "/home/build/ahriman" && \
|
||||
cp ./*-src.tar.xz "package/archlinux" && \
|
||||
cd "package/archlinux" && \
|
||||
runuser -u build -- makepkg --noconfirm --install --skipchecksums && \
|
||||
cd - && rm -r "/home/build/ahriman"
|
||||
cd / && rm -r "/home/build/ahriman"
|
||||
|
||||
VOLUME ["/var/lib/ahriman"]
|
||||
|
||||
|
18
Makefile
18
Makefile
@ -1,9 +1,9 @@
|
||||
.PHONY: archive archive_directory archlinux check clean directory push spec spec-html tests version
|
||||
.PHONY: archive archlinux check clean directory html push specification tests version
|
||||
.DEFAULT_GOAL := archlinux
|
||||
|
||||
PROJECT := ahriman
|
||||
|
||||
FILES := AUTHORS COPYING README.md docs package src setup.py tox.ini web.png
|
||||
FILES := AUTHORS CONTRIBUTING.md COPYING README.md docs package src setup.py tox.ini web.png
|
||||
TARGET_FILES := $(addprefix $(PROJECT)/, $(FILES))
|
||||
IGNORE_FILES := package/archlinux src/.mypy_cache
|
||||
|
||||
@ -33,22 +33,22 @@ clean:
|
||||
directory: clean
|
||||
mkdir "$(PROJECT)"
|
||||
|
||||
push: spec archlinux
|
||||
html: specification
|
||||
rm -rf docs/html
|
||||
tox -e docs-html
|
||||
|
||||
push: specification archlinux
|
||||
git add package/archlinux/PKGBUILD src/ahriman/version.py docs/ahriman-architecture.svg docs/ahriman.1
|
||||
git commit -m "Release $(VERSION)"
|
||||
git tag "$(VERSION)"
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
spec:
|
||||
specification:
|
||||
# make sure that old files are removed
|
||||
find docs -type f -name "$(PROJECT)*.rst" -delete
|
||||
tox -e docs
|
||||
|
||||
spec-html: spec
|
||||
rm -rf docs/html
|
||||
tox -e docs-html
|
||||
|
||||
tests: clean
|
||||
tox -e tests
|
||||
|
||||
@ -56,4 +56,4 @@ version:
|
||||
ifndef VERSION
|
||||
$(error VERSION is required, but not set)
|
||||
endif
|
||||
sed -i '/__version__ = .*/s/[^"][^)]*/__version__ = "$(VERSION)"/' src/ahriman/version.py
|
||||
sed -i 's/^__version__ = .*/__version__ = "$(VERSION)"/' src/ahriman/version.py
|
||||
|
19
README.md
19
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/arcan1s/ahriman/actions/workflows/run-tests.yml)
|
||||
[](https://github.com/arcan1s/ahriman/actions/workflows/run-setup.yml)
|
||||
[](https://github.com/arcan1s/ahriman/actions/workflows/docker-image.yml)
|
||||
[](https://hub.docker.com/r/arcan1s/ahriman)
|
||||
[](https://www.codefactor.io/repository/github/arcan1s/ahriman)
|
||||
[](https://ahriman.readthedocs.io/?badge=latest)
|
||||
|
||||
@ -10,23 +10,26 @@ Wrapper for managing custom repository inspired by [repo-scripts](https://github
|
||||
|
||||
## Features
|
||||
|
||||
* Install-configure-forget manager for own repository.
|
||||
* Install-configure-forget manager for the very own repository.
|
||||
* Multi-architecture support.
|
||||
* VCS packages support.
|
||||
* Sign support with gpg (repository, package, per package settings).
|
||||
* Synchronization to remote services (rsync, s3 and github) and report generation (email, html, telegram) and even ability to write own extensions.
|
||||
* Dependency manager.
|
||||
* VCS packages support.
|
||||
* Official repository support.
|
||||
* Ability to patch AUR packages and even create package from local PKGBUILDs.
|
||||
* Sign support with gpg (repository, package, per package settings).
|
||||
* 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:
|
||||
|
||||

|
||||
|
||||
## Installation and run
|
||||
|
||||
For installation details please refer to the [documentation](docs/setup.rst). For command help, `--help` subcommand must be used. Subcommands have own help message as well. The package also provides a [man page](docs/ahriman.1).
|
||||
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)).
|
||||
|
||||
## Configuration
|
||||
|
||||
Every available option is described in the [documentation](docs/configuration.rst).
|
||||
Every available option is described in the [documentation](https://ahriman.readthedocs.io/en/latest/configuration.html).
|
||||
|
||||
## [FAQ](docs/faq.rst)
|
||||
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)
|
||||
|
@ -33,7 +33,7 @@ fi
|
||||
ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" repo-setup "${AHRIMAN_SETUP_ARGS[@]}"
|
||||
|
||||
# refresh database
|
||||
runuser -u build -- yay --noconfirm -Syy &> /dev/null
|
||||
pacman -Syy &> /dev/null
|
||||
# create machine-id which is required by build tools
|
||||
systemd-machine-id-setup &> /dev/null
|
||||
|
||||
|
12
docker/install-aur-package.sh
Executable file
12
docker/install-aur-package.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
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
|
||||
cd /
|
||||
rm -r "$BUILD_DIR"
|
||||
done
|
@ -37,7 +37,6 @@ And the ``repository`` instance can be used to perform repository maintenance
|
||||
built_packages = repository.packages_built()
|
||||
update_result = repository.process_update(built_packages)
|
||||
|
||||
repository.process_report(None, update_result)
|
||||
repository.process_sync(None, update_result.success)
|
||||
repository.process_triggers(update_result)
|
||||
|
||||
For the more info please refer to the classes documentation.
|
||||
|
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 517 KiB After Width: | Height: | Size: 613 KiB |
439
docs/ahriman.1
439
docs/ahriman.1
@ -1,11 +1,11 @@
|
||||
.TH AHRIMAN "1" Manual
|
||||
.TH AHRIMAN "1" "2022\-11\-11" "ahriman" "Generated Python Manual"
|
||||
.SH NAME
|
||||
ahriman
|
||||
.SH SYNOPSIS
|
||||
.B ahriman
|
||||
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--no-report] [-q] [--unsafe] [-v] {aur-search,search,help,help-commands-unsafe,key-import,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,repo-backup,repo-check,check,repo-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-triggers,repo-update,update,user-add,user-list,user-remove,web} ...
|
||||
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,daemon,help,help-commands-unsafe,key-import,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-triggers,repo-update,update,shell,user-add,user-list,user-remove,version,web} ...
|
||||
.SH DESCRIPTION
|
||||
ArcH Linux ReposItory MANager
|
||||
ArcH linux ReposItory MANager
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
@ -25,8 +25,8 @@ force run, remove file lock
|
||||
lock file
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-report\fR
|
||||
force disable reporting to web service
|
||||
\fB\-\-report\fR, \fB\-\-no\-report\fR
|
||||
force enable or disable reporting to web service (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
@ -37,122 +37,172 @@ force disable any logging
|
||||
allow to run ahriman as non\-ahriman user. Some actions might be unavailable
|
||||
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
show program's version number and exit
|
||||
|
||||
.SH
|
||||
COMMAND
|
||||
.TP
|
||||
\fBahriman\fR \fI\,aur-search\/\fR
|
||||
\fBahriman\fR \fI\,aur\-search\/\fR
|
||||
search for package
|
||||
.TP
|
||||
\fBahriman\fR \fI\,daemon\/\fR
|
||||
run application as daemon
|
||||
.TP
|
||||
\fBahriman\fR \fI\,help\/\fR
|
||||
show help message
|
||||
.TP
|
||||
\fBahriman\fR \fI\,help-commands-unsafe\/\fR
|
||||
\fBahriman\fR \fI\,help\-commands\-unsafe\/\fR
|
||||
list unsafe commands
|
||||
.TP
|
||||
\fBahriman\fR \fI\,key-import\/\fR
|
||||
\fBahriman\fR \fI\,key\-import\/\fR
|
||||
import PGP key
|
||||
.TP
|
||||
\fBahriman\fR \fI\,package-add\/\fR
|
||||
\fBahriman\fR \fI\,package\-add\/\fR
|
||||
add package
|
||||
.TP
|
||||
\fBahriman\fR \fI\,package-remove\/\fR
|
||||
\fBahriman\fR \fI\,package\-remove\/\fR
|
||||
remove package
|
||||
.TP
|
||||
\fBahriman\fR \fI\,package-status\/\fR
|
||||
\fBahriman\fR \fI\,package\-status\/\fR
|
||||
get package status
|
||||
.TP
|
||||
\fBahriman\fR \fI\,package-status-remove\/\fR
|
||||
\fBahriman\fR \fI\,package\-status\-remove\/\fR
|
||||
remove package status
|
||||
.TP
|
||||
\fBahriman\fR \fI\,package-status-update\/\fR
|
||||
\fBahriman\fR \fI\,package\-status\-update\/\fR
|
||||
update package status
|
||||
.TP
|
||||
\fBahriman\fR \fI\,patch-add\/\fR
|
||||
add patch set
|
||||
\fBahriman\fR \fI\,patch\-add\/\fR
|
||||
add patch for PKGBUILD function
|
||||
.TP
|
||||
\fBahriman\fR \fI\,patch-list\/\fR
|
||||
\fBahriman\fR \fI\,patch\-list\/\fR
|
||||
list patch sets
|
||||
.TP
|
||||
\fBahriman\fR \fI\,patch-remove\/\fR
|
||||
\fBahriman\fR \fI\,patch\-remove\/\fR
|
||||
remove patch set
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-backup\/\fR
|
||||
\fBahriman\fR \fI\,patch\-set\-add\/\fR
|
||||
add patch set
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo\-backup\/\fR
|
||||
backup repository data
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-check\/\fR
|
||||
\fBahriman\fR \fI\,repo\-check\/\fR
|
||||
check for updates
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-clean\/\fR
|
||||
\fBahriman\fR \fI\,repo\-clean\/\fR
|
||||
clean local caches
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-config\/\fR
|
||||
\fBahriman\fR \fI\,repo\-config\/\fR
|
||||
dump configuration
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-rebuild\/\fR
|
||||
\fBahriman\fR \fI\,repo\-rebuild\/\fR
|
||||
rebuild repository
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-remove-unknown\/\fR
|
||||
\fBahriman\fR \fI\,repo\-remove\-unknown\/\fR
|
||||
remove unknown packages
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-restore\/\fR
|
||||
\fBahriman\fR \fI\,repo\-report\/\fR
|
||||
generate report
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo\-restore\/\fR
|
||||
restore repository data
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-setup\/\fR
|
||||
\fBahriman\fR \fI\,repo\-setup\/\fR
|
||||
initial service configuration
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-sign\/\fR
|
||||
\fBahriman\fR \fI\,repo\-sign\/\fR
|
||||
sign packages
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-status-update\/\fR
|
||||
\fBahriman\fR \fI\,repo\-status\-update\/\fR
|
||||
update repository status
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-triggers\/\fR
|
||||
\fBahriman\fR \fI\,repo\-sync\/\fR
|
||||
sync repository
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo\-triggers\/\fR
|
||||
run triggers
|
||||
.TP
|
||||
\fBahriman\fR \fI\,repo-update\/\fR
|
||||
\fBahriman\fR \fI\,repo\-update\/\fR
|
||||
update packages
|
||||
.TP
|
||||
\fBahriman\fR \fI\,user-add\/\fR
|
||||
\fBahriman\fR \fI\,shell\/\fR
|
||||
envoke python shell
|
||||
.TP
|
||||
\fBahriman\fR \fI\,user\-add\/\fR
|
||||
create or update user
|
||||
.TP
|
||||
\fBahriman\fR \fI\,user-list\/\fR
|
||||
\fBahriman\fR \fI\,user\-list\/\fR
|
||||
user known users and their access
|
||||
.TP
|
||||
\fBahriman\fR \fI\,user-remove\/\fR
|
||||
\fBahriman\fR \fI\,user\-remove\/\fR
|
||||
remove user
|
||||
.TP
|
||||
\fBahriman\fR \fI\,version\/\fR
|
||||
application version
|
||||
.TP
|
||||
\fBahriman\fR \fI\,web\/\fR
|
||||
web server
|
||||
.SH COMMAND \fI\,'ahriman aur-search'\/\fR
|
||||
usage: ahriman aur-search [-h] [-e] [-i]
|
||||
[--sort-by {description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}]
|
||||
|
||||
.SH COMMAND \fI\,'ahriman aur\-search'\/\fR
|
||||
usage: ahriman aur\-search [\-h] [\-e] [\-\-info | \-\-no\-info]
|
||||
[\-\-sort\-by {description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}]
|
||||
search [search ...]
|
||||
|
||||
search for package in AUR using API
|
||||
|
||||
.TP
|
||||
\fBsearch\fR
|
||||
search terms, can be specified multiple times, result will match all terms
|
||||
search terms, can be specified multiple times, the result will match all terms
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman aur-search'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman aur\-search'\/\fR
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||
return non\-zero exit status if result is empty
|
||||
|
||||
.TP
|
||||
\fB\-i\fR, \fB\-\-info\fR
|
||||
show additional package information
|
||||
\fB\-\-info\fR, \fB\-\-no\-info\fR
|
||||
show additional package information (default: False)
|
||||
|
||||
.TP
|
||||
\fB\-\-sort\-by\fR \fI\,{description,first_submitted,id,last_modified,maintainer,name,num_votes,out_of_date,package_base,package_base_id,popularity,repository,url,url_path,version}\/\fR
|
||||
sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted
|
||||
by name
|
||||
|
||||
.SH COMMAND \fI\,'ahriman daemon'\/\fR
|
||||
usage: ahriman daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
|
||||
[\-\-vcs | \-\-no\-vcs] [\-y]
|
||||
|
||||
start process which periodically will run update process
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman daemon'\/\fR
|
||||
.TP
|
||||
\fB\-i\fR \fI\,INTERVAL\/\fR, \fB\-\-interval\fR \fI\,INTERVAL\/\fR
|
||||
interval between runs in seconds
|
||||
|
||||
.TP
|
||||
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
|
||||
enable or disable checking for AUR updates. Implies \-\-no\-vcs (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-local\fR, \fB\-\-no\-local\fR
|
||||
enable or disable checking of local packages for updates (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
|
||||
include or exclude manual updates (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
|
||||
enable or disable checking of VCS packages (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-refresh\fR
|
||||
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
|
||||
|
||||
.SH COMMAND \fI\,'ahriman help'\/\fR
|
||||
usage: ahriman help [-h] [command]
|
||||
usage: ahriman help [\-h] [command]
|
||||
|
||||
show help message for application or command and exit
|
||||
|
||||
@ -160,19 +210,19 @@ show help message for application or command and exit
|
||||
\fBcommand\fR
|
||||
show help message for specific command
|
||||
|
||||
.SH COMMAND \fI\,'ahriman help-commands-unsafe'\/\fR
|
||||
usage: ahriman help-commands-unsafe [-h] [--command COMMAND]
|
||||
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
|
||||
usage: ahriman help\-commands\-unsafe [\-h] [\-\-command COMMAND]
|
||||
|
||||
list unsafe commands as defined in default args
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman help-commands-unsafe'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman help\-commands\-unsafe'\/\fR
|
||||
.TP
|
||||
\fB\-\-command\fR \fI\,COMMAND\/\fR
|
||||
instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1
|
||||
otherwise
|
||||
|
||||
.SH COMMAND \fI\,'ahriman key-import'\/\fR
|
||||
usage: ahriman key-import [-h] [--key-server KEY_SERVER] key
|
||||
.SH COMMAND \fI\,'ahriman key\-import'\/\fR
|
||||
usage: ahriman key\-import [\-h] [\-\-key\-server KEY_SERVER] key
|
||||
|
||||
import PGP key from public sources to the repository user
|
||||
|
||||
@ -180,15 +230,14 @@ import PGP key from public sources to the repository user
|
||||
\fBkey\fR
|
||||
PGP key to import from public server
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman key-import'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman key\-import'\/\fR
|
||||
.TP
|
||||
\fB\-\-key\-server\fR \fI\,KEY_SERVER\/\fR
|
||||
key server for key import
|
||||
|
||||
.SH COMMAND \fI\,'ahriman package-add'\/\fR
|
||||
usage: ahriman package-add [-h] [-e] [-n]
|
||||
[-s {PackageSource.Auto,PackageSource.Archive,PackageSource.AUR,PackageSource.Directory,PackageSource.Local,PackageSource.Remote,PackageSource.Repository}]
|
||||
[--without-dependencies]
|
||||
.SH COMMAND \fI\,'ahriman package\-add'\/\fR
|
||||
usage: ahriman package\-add [\-h] [\-e] [\-n] [\-y] [\-s {auto,archive,aur,directory,local,remote,repository}]
|
||||
[\-\-without\-dependencies]
|
||||
package [package ...]
|
||||
|
||||
add existing or new package to the build queue
|
||||
@ -197,7 +246,7 @@ add existing or new package to the build queue
|
||||
\fBpackage\fR
|
||||
package source (base name, path to local files, remote URL)
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman package-add'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman package\-add'\/\fR
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||
return non\-zero exit status if result is empty
|
||||
@ -207,15 +256,19 @@ return non\-zero exit status if result is empty
|
||||
run update function after
|
||||
|
||||
.TP
|
||||
\fB\-s\fR \fI\,{PackageSource.Auto,PackageSource.Archive,PackageSource.AUR,PackageSource.Directory,PackageSource.Local,PackageSource.Remote,PackageSource.Repository}\/\fR, \fB\-\-source\fR \fI\,{PackageSource.Auto,PackageSource.Archive,PackageSource.AUR,PackageSource.Directory,PackageSource.Local,PackageSource.Remote,PackageSource.Repository}\/\fR
|
||||
\fB\-y\fR, \fB\-\-refresh\fR
|
||||
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
|
||||
|
||||
.TP
|
||||
\fB\-s\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR, \fB\-\-source\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR
|
||||
explicitly specify the package source for this command
|
||||
|
||||
.TP
|
||||
\fB\-\-without\-dependencies\fR
|
||||
do not add dependencies
|
||||
|
||||
.SH COMMAND \fI\,'ahriman package-remove'\/\fR
|
||||
usage: ahriman package-remove [-h] package [package ...]
|
||||
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
|
||||
usage: ahriman package\-remove [\-h] package [package ...]
|
||||
|
||||
remove package from the repository
|
||||
|
||||
@ -223,10 +276,8 @@ remove package from the repository
|
||||
\fBpackage\fR
|
||||
package name or base
|
||||
|
||||
.SH COMMAND \fI\,'ahriman package-status'\/\fR
|
||||
usage: ahriman package-status [-h] [--ahriman] [-e] [-i]
|
||||
[-s {BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}]
|
||||
[package ...]
|
||||
.SH COMMAND \fI\,'ahriman package\-status'\/\fR
|
||||
usage: ahriman package\-status [\-h] [\-\-ahriman] [\-e] [\-i] [\-s {unknown,pending,building,failed,success}] [package ...]
|
||||
|
||||
request status of the package
|
||||
|
||||
@ -234,7 +285,7 @@ request status of the package
|
||||
\fBpackage\fR
|
||||
filter status by package base
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman package-status'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman package\-status'\/\fR
|
||||
.TP
|
||||
\fB\-\-ahriman\fR
|
||||
get service status itself
|
||||
@ -248,22 +299,20 @@ return non\-zero exit status if result is empty
|
||||
show additional package information
|
||||
|
||||
.TP
|
||||
\fB\-s\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR, \fB\-\-status\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR
|
||||
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
|
||||
filter packages by status
|
||||
|
||||
.SH COMMAND \fI\,'ahriman package-status-remove'\/\fR
|
||||
usage: ahriman package-status-remove [-h] package [package ...]
|
||||
.SH COMMAND \fI\,'ahriman package\-status\-remove'\/\fR
|
||||
usage: ahriman package\-status\-remove [\-h] package [package ...]
|
||||
|
||||
remove the package from the status page
|
||||
|
||||
.TP
|
||||
\fBpackage\fR
|
||||
remove specified packages
|
||||
remove specified packages from status page
|
||||
|
||||
.SH COMMAND \fI\,'ahriman package-status-update'\/\fR
|
||||
usage: ahriman package-status-update [-h]
|
||||
[-s {BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}]
|
||||
[package ...]
|
||||
.SH COMMAND \fI\,'ahriman package\-status\-update'\/\fR
|
||||
usage: ahriman package\-status\-update [\-h] [\-s {unknown,pending,building,failed,success}] [package ...]
|
||||
|
||||
update package status on the status page
|
||||
|
||||
@ -271,27 +320,30 @@ update package status on the status page
|
||||
\fBpackage\fR
|
||||
set status for specified packages. If no packages supplied, service status will be updated
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman package-status-update'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman package\-status\-update'\/\fR
|
||||
.TP
|
||||
\fB\-s\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR, \fB\-\-status\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR
|
||||
new status
|
||||
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
|
||||
new package build status
|
||||
|
||||
.SH COMMAND \fI\,'ahriman patch-add'\/\fR
|
||||
usage: ahriman patch-add [-h] [-t TRACK] package
|
||||
.SH COMMAND \fI\,'ahriman patch\-add'\/\fR
|
||||
usage: ahriman patch\-add [\-h] package variable [patch]
|
||||
|
||||
create or update source patches
|
||||
create or update patched PKGBUILD function or variable
|
||||
|
||||
.TP
|
||||
\fBpackage\fR
|
||||
path to directory with changed files for patch addition/update
|
||||
package base
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman patch-add'\/\fR
|
||||
.TP
|
||||
\fB\-t\fR \fI\,TRACK\/\fR, \fB\-\-track\fR \fI\,TRACK\/\fR
|
||||
files which has to be tracked
|
||||
\fBvariable\fR
|
||||
PKGBUILD variable or function name. If variable is a function, it must end with ()
|
||||
|
||||
.SH COMMAND \fI\,'ahriman patch-list'\/\fR
|
||||
usage: ahriman patch-list [-h] [-e] [package]
|
||||
.TP
|
||||
\fBpatch\fR
|
||||
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]
|
||||
|
||||
list available patches for the package
|
||||
|
||||
@ -299,13 +351,17 @@ list available patches for the package
|
||||
\fBpackage\fR
|
||||
package base
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman patch-list'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman patch\-list'\/\fR
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||
return non\-zero exit status if result is empty
|
||||
|
||||
.SH COMMAND \fI\,'ahriman patch-remove'\/\fR
|
||||
usage: ahriman patch-remove [-h] package
|
||||
.TP
|
||||
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
|
||||
if set, show only patches for specified PKGBUILD variables
|
||||
|
||||
.SH COMMAND \fI\,'ahriman patch\-remove'\/\fR
|
||||
usage: ahriman patch\-remove [\-h] [\-v VARIABLE] package
|
||||
|
||||
remove patches for the package
|
||||
|
||||
@ -313,69 +369,98 @@ remove patches for the package
|
||||
\fBpackage\fR
|
||||
package base
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-backup'\/\fR
|
||||
usage: ahriman repo-backup [-h] path
|
||||
.SH OPTIONS \fI\,'ahriman patch\-remove'\/\fR
|
||||
.TP
|
||||
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
|
||||
should be used for single\-function patches in case if you wold like to remove only specified PKGBUILD variables. In case
|
||||
if not set, it will remove all patches related to the package
|
||||
|
||||
backup settings and database
|
||||
.SH COMMAND \fI\,'ahriman patch\-set\-add'\/\fR
|
||||
usage: ahriman patch\-set\-add [\-h] [\-t TRACK] package
|
||||
|
||||
create or update source patches
|
||||
|
||||
.TP
|
||||
\fBpackage\fR
|
||||
path to directory with changed files for patch addition/update
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman patch\-set\-add'\/\fR
|
||||
.TP
|
||||
\fB\-t\fR \fI\,TRACK\/\fR, \fB\-\-track\fR \fI\,TRACK\/\fR
|
||||
files which has to be tracked
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo\-backup'\/\fR
|
||||
usage: ahriman repo\-backup [\-h] path
|
||||
|
||||
backup repository settings and database
|
||||
|
||||
.TP
|
||||
\fBpath\fR
|
||||
path of the output archive
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-check'\/\fR
|
||||
usage: ahriman repo-check [-h] [-e] [--no-vcs] [package ...]
|
||||
.SH COMMAND \fI\,'ahriman repo\-check'\/\fR
|
||||
usage: ahriman repo\-check [\-h] [\-e] [\-\-vcs | \-\-no\-vcs] [\-y] [package ...]
|
||||
|
||||
check for packages updates. Same as update \-\-dry\-run \-\-no\-manual
|
||||
check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
|
||||
|
||||
.TP
|
||||
\fBpackage\fR
|
||||
filter check by package base
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-check'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-check'\/\fR
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||
return non\-zero exit status if result is empty
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-vcs\fR
|
||||
do not check VCS packages
|
||||
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
|
||||
enable or disable checking of VCS packages (default: True)
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-clean'\/\fR
|
||||
usage: ahriman repo-clean [-h] [--cache] [--chroot] [--manual] [--packages]
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-refresh\fR
|
||||
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo\-clean'\/\fR
|
||||
usage: ahriman repo\-clean [\-h] [\-\-cache | \-\-no\-cache] [\-\-chroot | \-\-no\-chroot] [\-\-manual | \-\-no\-manual]
|
||||
[\-\-packages | \-\-no\-packages] [\-\-pacman | \-\-no\-pacman]
|
||||
|
||||
remove local caches
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-clean'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-clean'\/\fR
|
||||
.TP
|
||||
\fB\-\-cache\fR
|
||||
clear directory with package caches
|
||||
\fB\-\-cache\fR, \fB\-\-no\-cache\fR
|
||||
clear directory with package caches (default: False)
|
||||
|
||||
.TP
|
||||
\fB\-\-chroot\fR
|
||||
clear build chroot
|
||||
\fB\-\-chroot\fR, \fB\-\-no\-chroot\fR
|
||||
clear build chroot (default: False)
|
||||
|
||||
.TP
|
||||
\fB\-\-manual\fR
|
||||
clear manually added packages queue
|
||||
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
|
||||
clear manually added packages queue (default: False)
|
||||
|
||||
.TP
|
||||
\fB\-\-packages\fR
|
||||
clear directory with built packages
|
||||
\fB\-\-packages\fR, \fB\-\-no\-packages\fR
|
||||
clear directory with built packages (default: False)
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-config'\/\fR
|
||||
usage: ahriman repo-config [-h]
|
||||
.TP
|
||||
\fB\-\-pacman\fR, \fB\-\-no\-pacman\fR
|
||||
clear directory with pacman local database cache (default: False)
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo\-config'\/\fR
|
||||
usage: ahriman repo\-config [\-h]
|
||||
|
||||
dump configuration for the specified architecture
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-rebuild'\/\fR
|
||||
usage: ahriman repo-rebuild [-h] [--depends-on DEPENDS_ON] [--dry-run] [--from-database] [-e]
|
||||
.SH COMMAND \fI\,'ahriman repo\-rebuild'\/\fR
|
||||
usage: ahriman repo\-rebuild [\-h] [\-\-depends\-on DEPENDS_ON] [\-\-dry\-run] [\-\-from\-database] [\-e]
|
||||
|
||||
force rebuild whole repository
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-rebuild'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-rebuild'\/\fR
|
||||
.TP
|
||||
\fB\-\-depends\-on\fR \fI\,DEPENDS_ON\/\fR
|
||||
only rebuild packages that depend on specified package
|
||||
only rebuild packages that depend on specified packages
|
||||
|
||||
.TP
|
||||
\fB\-\-dry\-run\fR
|
||||
@ -384,29 +469,30 @@ just perform check for packages without rebuild process itself
|
||||
.TP
|
||||
\fB\-\-from\-database\fR
|
||||
read packages from database instead of filesystem. This feature in particular is required in case if you would like to
|
||||
restore repository from another repository instance. Note however that in order to restore packages you need to have
|
||||
restore repository from another repository instance. Note, however, that in order to restore packages you need to have
|
||||
original ahriman instance run with web service and have run repo\-update at least once.
|
||||
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||
return non\-zero exit status if result is empty
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-remove-unknown'\/\fR
|
||||
usage: ahriman repo-remove-unknown [-h] [--dry-run] [-i]
|
||||
.SH COMMAND \fI\,'ahriman repo\-remove\-unknown'\/\fR
|
||||
usage: ahriman repo\-remove\-unknown [\-h] [\-\-dry\-run]
|
||||
|
||||
remove packages which are missing in AUR and do not have local PKGBUILDs
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-remove-unknown'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-remove\-unknown'\/\fR
|
||||
.TP
|
||||
\fB\-\-dry\-run\fR
|
||||
just perform check for packages without removal
|
||||
|
||||
.TP
|
||||
\fB\-i\fR, \fB\-\-info\fR
|
||||
show additional package information
|
||||
.SH COMMAND \fI\,'ahriman repo\-report'\/\fR
|
||||
usage: ahriman repo\-report [\-h]
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-restore'\/\fR
|
||||
usage: ahriman repo-restore [-h] [-o OUTPUT] path
|
||||
generate repository report according to current settings
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo\-restore'\/\fR
|
||||
usage: ahriman repo\-restore [\-h] [\-o OUTPUT] path
|
||||
|
||||
restore settings and database
|
||||
|
||||
@ -414,21 +500,20 @@ restore settings and database
|
||||
\fBpath\fR
|
||||
path of the input archive
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-restore'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-restore'\/\fR
|
||||
.TP
|
||||
\fB\-o\fR \fI\,OUTPUT\/\fR, \fB\-\-output\fR \fI\,OUTPUT\/\fR
|
||||
root path of the extracted files
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-setup'\/\fR
|
||||
usage: ahriman repo-setup [-h] [--build-as-user BUILD_AS_USER] [--build-command BUILD_COMMAND]
|
||||
[--from-configuration FROM_CONFIGURATION] [--no-multilib] --packager PACKAGER --repository
|
||||
REPOSITORY [--sign-key SIGN_KEY]
|
||||
[--sign-target {SignSettings.Disabled,SignSettings.Packages,SignSettings.Repository}]
|
||||
[--web-port WEB_PORT]
|
||||
.SH COMMAND \fI\,'ahriman repo\-setup'\/\fR
|
||||
usage: ahriman repo\-setup [\-h] [\-\-build\-as\-user BUILD_AS_USER] [\-\-build\-command BUILD_COMMAND]
|
||||
[\-\-from\-configuration FROM_CONFIGURATION] [\-\-multilib | \-\-no\-multilib] \-\-packager PACKAGER
|
||||
\-\-repository REPOSITORY [\-\-sign\-key SIGN_KEY] [\-\-sign\-target {disabled,pacakges,repository}]
|
||||
[\-\-web\-port WEB_PORT]
|
||||
|
||||
create initial service configuration, requires root
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-setup'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-setup'\/\fR
|
||||
.TP
|
||||
\fB\-\-build\-as\-user\fR \fI\,BUILD_AS_USER\/\fR
|
||||
force makepkg user to the specific one
|
||||
@ -442,8 +527,8 @@ build command prefix
|
||||
path to default devtools pacman configuration
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-multilib\fR
|
||||
do not add multilib repository
|
||||
\fB\-\-multilib\fR, \fB\-\-no\-multilib\fR
|
||||
add or do not multilib repository (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
|
||||
@ -458,15 +543,15 @@ repository name
|
||||
sign key id
|
||||
|
||||
.TP
|
||||
\fB\-\-sign\-target\fR \fI\,{SignSettings.Disabled,SignSettings.Packages,SignSettings.Repository}\/\fR
|
||||
\fB\-\-sign\-target\fR \fI\,{disabled,pacakges,repository}\/\fR
|
||||
sign options
|
||||
|
||||
.TP
|
||||
\fB\-\-web\-port\fR \fI\,WEB_PORT\/\fR
|
||||
port of the web service
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-sign'\/\fR
|
||||
usage: ahriman repo-sign [-h] [package ...]
|
||||
.SH COMMAND \fI\,'ahriman repo\-sign'\/\fR
|
||||
usage: ahriman repo\-sign [\-h] [package ...]
|
||||
|
||||
(re\-)sign packages and repository database according to current settings
|
||||
|
||||
@ -474,24 +559,34 @@ usage: ahriman repo-sign [-h] [package ...]
|
||||
\fBpackage\fR
|
||||
sign only specified packages
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-status-update'\/\fR
|
||||
usage: ahriman repo-status-update [-h]
|
||||
[-s {BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}]
|
||||
.SH COMMAND \fI\,'ahriman repo\-status\-update'\/\fR
|
||||
usage: ahriman repo\-status\-update [\-h] [\-s {unknown,pending,building,failed,success}]
|
||||
|
||||
update repository status on the status page
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-status-update'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-status\-update'\/\fR
|
||||
.TP
|
||||
\fB\-s\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR, \fB\-\-status\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR
|
||||
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
|
||||
new status
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-triggers'\/\fR
|
||||
usage: ahriman repo-triggers [-h]
|
||||
.SH COMMAND \fI\,'ahriman repo\-sync'\/\fR
|
||||
usage: ahriman repo\-sync [\-h]
|
||||
|
||||
sync repository files to remote server according to current settings
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo\-triggers'\/\fR
|
||||
usage: ahriman repo\-triggers [\-h] [trigger ...]
|
||||
|
||||
run triggers on empty build result as configured by settings
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo-update'\/\fR
|
||||
usage: ahriman repo-update [-h] [--dry-run] [-e] [--no-aur] [--no-local] [--no-manual] [--no-vcs] [package ...]
|
||||
.TP
|
||||
\fBtrigger\fR
|
||||
instead of running all triggers as set by configuration, just process specified ones oin order of metion
|
||||
|
||||
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
|
||||
usage: ahriman repo\-update [\-h] [\-\-dry\-run] [\-e] [\-\-aur | \-\-no\-aur] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
|
||||
[\-\-vcs | \-\-no\-vcs] [\-y]
|
||||
[package ...]
|
||||
|
||||
check for packages updates and run build process if requested
|
||||
|
||||
@ -499,7 +594,7 @@ check for packages updates and run build process if requested
|
||||
\fBpackage\fR
|
||||
filter check by package base
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman repo-update'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman repo\-update'\/\fR
|
||||
.TP
|
||||
\fB\-\-dry\-run\fR
|
||||
just perform check for updates, same as check command
|
||||
@ -509,24 +604,36 @@ just perform check for updates, same as check command
|
||||
return non\-zero exit status if result is empty
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-aur\fR
|
||||
do not check for AUR updates. Implies \-\-no\-vcs
|
||||
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
|
||||
enable or disable checking for AUR updates. Implies \-\-no\-vcs (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-local\fR
|
||||
do not check local packages for updates
|
||||
\fB\-\-local\fR, \fB\-\-no\-local\fR
|
||||
enable or disable checking of local packages for updates (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-manual\fR
|
||||
do not include manual updates
|
||||
\fB\-\-manual\fR, \fB\-\-no\-manual\fR
|
||||
include or exclude manual updates (default: True)
|
||||
|
||||
.TP
|
||||
\fB\-\-no\-vcs\fR
|
||||
do not check VCS packages
|
||||
\fB\-\-vcs\fR, \fB\-\-no\-vcs\fR
|
||||
enable or disable checking of VCS packages (default: True)
|
||||
|
||||
.SH COMMAND \fI\,'ahriman user-add'\/\fR
|
||||
usage: ahriman user-add [-h] [--as-service] [-p PASSWORD] [-r {UserAccess.Safe,UserAccess.Read,UserAccess.Write}] [-s]
|
||||
username
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-refresh\fR
|
||||
download fresh package databases from the mirror before actions, \-yy to force refresh even if up to date
|
||||
|
||||
.SH COMMAND \fI\,'ahriman shell'\/\fR
|
||||
usage: ahriman shell [\-h] [code]
|
||||
|
||||
drop into python shell while having created application
|
||||
|
||||
.TP
|
||||
\fBcode\fR
|
||||
instead of dropping into shell, just execute the specified code
|
||||
|
||||
.SH COMMAND \fI\,'ahriman user\-add'\/\fR
|
||||
usage: ahriman user\-add [\-h] [\-\-as\-service] [\-p PASSWORD] [\-r {unauthorized,read,reporter,full}] [\-s] username
|
||||
|
||||
update user for web services with the given password and role. In case if password was not entered it will be asked interactively
|
||||
|
||||
@ -534,7 +641,7 @@ update user for web services with the given password and role. In case if passwo
|
||||
\fBusername\fR
|
||||
username for web service
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman user-add'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman user\-add'\/\fR
|
||||
.TP
|
||||
\fB\-\-as\-service\fR
|
||||
add user as service user
|
||||
@ -545,15 +652,15 @@ user password. Blank password will be treated as empty password, which is in par
|
||||
authorization type.
|
||||
|
||||
.TP
|
||||
\fB\-r\fR \fI\,{UserAccess.Safe,UserAccess.Read,UserAccess.Write}\/\fR, \fB\-\-role\fR \fI\,{UserAccess.Safe,UserAccess.Read,UserAccess.Write}\/\fR
|
||||
\fB\-r\fR \fI\,{unauthorized,read,reporter,full}\/\fR, \fB\-\-role\fR \fI\,{unauthorized,read,reporter,full}\/\fR
|
||||
user access level
|
||||
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-secure\fR
|
||||
set file permissions to user\-only
|
||||
|
||||
.SH COMMAND \fI\,'ahriman user-list'\/\fR
|
||||
usage: ahriman user-list [-h] [-e] [-r {UserAccess.Safe,UserAccess.Read,UserAccess.Write}] [username]
|
||||
.SH COMMAND \fI\,'ahriman user\-list'\/\fR
|
||||
usage: ahriman user\-list [\-h] [\-e] [\-r {unauthorized,read,reporter,full}] [username]
|
||||
|
||||
list users from the user mapping and their roles
|
||||
|
||||
@ -561,17 +668,17 @@ list users from the user mapping and their roles
|
||||
\fBusername\fR
|
||||
filter users by username
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman user-list'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman user\-list'\/\fR
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||
return non\-zero exit status if result is empty
|
||||
|
||||
.TP
|
||||
\fB\-r\fR \fI\,{UserAccess.Safe,UserAccess.Read,UserAccess.Write}\/\fR, \fB\-\-role\fR \fI\,{UserAccess.Safe,UserAccess.Read,UserAccess.Write}\/\fR
|
||||
\fB\-r\fR \fI\,{unauthorized,read,reporter,full}\/\fR, \fB\-\-role\fR \fI\,{unauthorized,read,reporter,full}\/\fR
|
||||
filter users by role
|
||||
|
||||
.SH COMMAND \fI\,'ahriman user-remove'\/\fR
|
||||
usage: ahriman user-remove [-h] [-s] username
|
||||
.SH COMMAND \fI\,'ahriman user\-remove'\/\fR
|
||||
usage: ahriman user\-remove [\-h] [\-s] username
|
||||
|
||||
remove user from the user mapping and update the configuration
|
||||
|
||||
@ -579,13 +686,18 @@ remove user from the user mapping and update the configuration
|
||||
\fBusername\fR
|
||||
username for web service
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman user-remove'\/\fR
|
||||
.SH OPTIONS \fI\,'ahriman user\-remove'\/\fR
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-secure\fR
|
||||
set file permissions to user\-only
|
||||
|
||||
.SH COMMAND \fI\,'ahriman version'\/\fR
|
||||
usage: ahriman version [\-h]
|
||||
|
||||
print application and its dependencies versions
|
||||
|
||||
.SH COMMAND \fI\,'ahriman web'\/\fR
|
||||
usage: ahriman web [-h]
|
||||
usage: ahriman web [\-h]
|
||||
|
||||
start web server
|
||||
|
||||
@ -595,7 +707,8 @@ Argument list can also be read from file by using @ prefix.
|
||||
.SH AUTHORS
|
||||
.nf
|
||||
ahriman team
|
||||
.fi.nf
|
||||
.fi
|
||||
.nf
|
||||
|
||||
.fi
|
||||
|
||||
|
@ -28,6 +28,14 @@ ahriman.application.handlers.clean module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.daemon module
|
||||
------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.application.handlers.daemon
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.dump module
|
||||
----------------------------------------
|
||||
|
||||
@ -116,6 +124,14 @@ ahriman.application.handlers.setup module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.shell module
|
||||
-----------------------------------------
|
||||
|
||||
.. automodule:: ahriman.application.handlers.shell
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.sign module
|
||||
----------------------------------------
|
||||
|
||||
@ -172,6 +188,14 @@ ahriman.application.handlers.users module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.versions module
|
||||
--------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.application.handlers.versions
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.handlers.web module
|
||||
---------------------------------------
|
||||
|
||||
|
@ -20,6 +20,22 @@ ahriman.core.database.migrations.m001\_package\_source module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.database.migrations.m002\_user\_access module
|
||||
----------------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.database.migrations.m002_user_access
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.database.migrations.m003\_patch\_variables module
|
||||
--------------------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.database.migrations.m003_patch_variables
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
@ -36,6 +36,14 @@ ahriman.core.formatters.package\_printer module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.formatters.patch\_printer module
|
||||
---------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.formatters.patch_printer
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.formatters.printer module
|
||||
--------------------------------------
|
||||
|
||||
@ -76,6 +84,14 @@ ahriman.core.formatters.user\_printer module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.formatters.version\_printer module
|
||||
-----------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.formatters.version_printer
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
45
docs/ahriman.core.gitremote.rst
Normal file
45
docs/ahriman.core.gitremote.rst
Normal file
@ -0,0 +1,45 @@
|
||||
ahriman.core.gitremote package
|
||||
==============================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
ahriman.core.gitremote.remote\_pull module
|
||||
------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.gitremote.remote_pull
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.gitremote.remote\_pull\_trigger module
|
||||
---------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.gitremote.remote_pull_trigger
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.gitremote.remote\_push module
|
||||
------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.gitremote.remote_push
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.gitremote.remote\_push\_trigger module
|
||||
---------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.gitremote.remote_push_trigger
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: ahriman.core.gitremote
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
@ -12,6 +12,7 @@ Subpackages
|
||||
ahriman.core.build_tools
|
||||
ahriman.core.database
|
||||
ahriman.core.formatters
|
||||
ahriman.core.gitremote
|
||||
ahriman.core.report
|
||||
ahriman.core.repository
|
||||
ahriman.core.sign
|
||||
@ -38,6 +39,14 @@ ahriman.core.exceptions module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.lazy\_logging module
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.lazy_logging
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.spawn module
|
||||
-------------------------
|
||||
|
||||
|
@ -92,6 +92,14 @@ ahriman.models.package\_source module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.pkgbuild\_patch module
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: ahriman.models.pkgbuild_patch
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.property module
|
||||
------------------------------
|
||||
|
||||
|
@ -4,14 +4,6 @@ ahriman.web.views.status package
|
||||
Submodules
|
||||
----------
|
||||
|
||||
ahriman.web.views.status.ahriman module
|
||||
---------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.views.status.ahriman
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.views.status.package module
|
||||
---------------------------------------
|
||||
|
||||
|
@ -19,27 +19,29 @@ Full dependency diagram:
|
||||
``ahriman.application`` package
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This package contains application (aka executable) related classes and everything for that. 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.Handler`` class.
|
||||
This package contains application (aka executable) related classes and everything for that. 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.
|
||||
|
||||
``ahriman.application.application.application.Application`` (god class) is used for any interaction from parsers with repository, web etc. It is divided into multiple traits by functions (package related and repository related) in the same package.
|
||||
``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.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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This package contains everything which is required for any time of application run and separated to several packages:
|
||||
This package contains everything which is required for any time of application run and 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.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 called by ``load`` method.
|
||||
* ``ahriman.core.build_tools`` is a package which provides wrapper for ``devtools`` commands.
|
||||
* ``ahriman.core.database`` is everything including data and schema migrations for database.
|
||||
* ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers.
|
||||
* ``ahriman.core.report`` is a package with reporting classes. Usually it must be called by ``ahriman.core.report.report.Report.load`` method.
|
||||
* ``ahriman.core.repository`` contains several traits and base repository (``ahriman.core.repository.repository.Repository`` class) implementation.
|
||||
* ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly.
|
||||
* ``ahriman.core.report`` is a package with reporting triggers. Should not be called directly.
|
||||
* ``ahriman.core.repository`` contains several traits and base repository (``ahriman.core.repository.Repository`` class) implementation.
|
||||
* ``ahriman.core.sign`` package provides sign feature (only gpg calls are available).
|
||||
* ``ahriman.core.status`` contains helpers and watcher class which are required for web application. Reporter must be initialized by using ``ahriman.core.status.client.Client.load`` method.
|
||||
* ``ahriman.core.upload`` package provides sync feature, must be called by ``ahriman.core.upload.upload.Upload.load`` method.
|
||||
* ``ahriman.core.triggers`` package contains base trigger classes. Classes from this package must be imported in order to implement user extensions. In fact, ``ahriman.core.report`` and ``ahriman.core.upload`` use this package.
|
||||
* ``ahriman.core.upload`` package provides sync feature, should not be called directly.
|
||||
|
||||
This package also provides some generic functions and classes which may be used by other packages:
|
||||
|
||||
@ -68,7 +70,7 @@ Application run
|
||||
|
||||
* Parse command line arguments, find command and related handler which is set by parser.
|
||||
* Call ``Handler.execute`` method.
|
||||
* Define list of architectures to run. In case if there is more than one architecture specified run several subprocesses or process in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether application can be run in multiple processes or not - this feature is required for some handlers (e.g. ``Web``) which should be able to spawn child process in daemon mode (it is impossible to do for daemonic processes).
|
||||
* Define list of architectures to run. In case if there is more than one architecture specified run several subprocesses or process in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether application can be run in multiple processes or not - this feature is required for some handlers (e.g. ``Web``) which should be able to spawn child process in daemon mode (it is impossible to do from daemonic processes).
|
||||
* In each child process call lock functions.
|
||||
* After success checks pass control to ``Handler.run`` method defined by specific handler class.
|
||||
* Return result (success or failure) of each subprocess and exit from application.
|
||||
@ -86,14 +88,14 @@ 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.Operations`` also provides generic methods for database access (e.g. row converters and transactional support).
|
||||
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).
|
||||
|
||||
The ``ahriman.core.database.sqlite.SQLite`` class itself derives from all of these traits and implements methods for initialization, including migrations.
|
||||
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 autmatically and applies them in alphabetical order.
|
||||
The schema migration are applied according to current ``pragma user_info`` values, located at ``ahriman.core.database.migrations`` package and named as ``m000_migration_name.py`` (the preceding ``m`` is required in order to import migration content for tests). Additional class ``ahriman.core.database.migrations.Migrations`` reads all migrations automatically and applies them in alphabetical order.
|
||||
|
||||
There are also data migrations which are located at ``ahriman.core.database.data`` package and move data from old-style (e.g. json files in filesystem, directory trees, etc) to the database. They are also part of migration and (unlike schema migrations) are applied only at specific version breakpoints (e.g. if ``user_version`` is more than 0 no initial migration will be applied).
|
||||
|
||||
@ -102,7 +104,7 @@ Type conversions
|
||||
|
||||
By default, it parses rows into python dictionary. In addition, the following pseudo-types are supported:
|
||||
|
||||
* ``Dict[str, Any]``, ``List[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` datatype and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
|
||||
* ``Dict[str, Any]``, ``List[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` data type and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
|
||||
|
||||
Basic flows
|
||||
-----------
|
||||
@ -114,7 +116,7 @@ Idea is to copy package to the directory from which it will be handled at the ne
|
||||
|
||||
* 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 iis 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.
|
||||
* 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.
|
||||
|
||||
@ -154,6 +156,13 @@ Configuration
|
||||
|
||||
``ahriman.core.configuration.Configuration`` class provides some additional methods (e.g. ``getpath`` and ``getlist``) and also combines multiple files into single configuration dictionary using architecture overrides. It is the recommended way to deal with settings.
|
||||
|
||||
Enumerations
|
||||
^^^^^^^^^^^^
|
||||
|
||||
All enumerations are derived from ``str`` and ``enum.Enum``. Integer enumerations 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).
|
||||
|
||||
In addition, some enumerations provide ``from_option`` class methods in order to allow some flexibility while reading configuration options.
|
||||
|
||||
Utils
|
||||
^^^^^
|
||||
|
||||
@ -194,12 +203,14 @@ In order to configure users there are special commands.
|
||||
Triggers
|
||||
^^^^^^^^
|
||||
|
||||
Triggers are extensions which can be used in order to perform any actions after the update process. The package provides two default extensions - one is report generation and another one is remote upload feature.
|
||||
Triggers are extensions which can be used in order to perform any actions on application start, after the update process and, finally, before the application exit. The package provides two default extensions - one is report generation and another one is remote upload feature.
|
||||
|
||||
The main idea is to load classes by their full path (e.g. ``ahriman.core.upload.UploadTrigger``) by using ``importlib``: get the last part of the import and treat it as class name, join remain part by ``.`` and interpret as module path, import module and extract attribute from it.
|
||||
|
||||
The loaded triggers will be called with ``ahriman.models.result.Result`` and ``List[Packages]`` arguments, which describes the process result and current repository packages respectively. Any exception raised will be suppressed and will generate an exception message in logs.
|
||||
|
||||
In addition triggers can implement ``on_start`` and ``on_stop`` actions which will be called on the application start and right before the application exit. The ``on_start`` action is usually being called from handlers directly in order to make sure that no trigger will be run when it is not required (e.g. on user management). As soon as ``on_start`` action is called, the additional flag will be set; ``ahriman.core.triggers.TriggerLoader`` class implements ``__del__`` method in which, if the flag is set, the ``on_stop`` actions will be called.
|
||||
|
||||
For more details how to deal with the triggers, refer to :doc:`documentation <triggers>` and modules descriptions.
|
||||
|
||||
Remote synchronization
|
||||
@ -262,7 +273,7 @@ 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.
|
||||
|
||||
If this feature is configured any request will be prohibited without authentication. In addition, configuration flag ``auth.safe_build_status`` can be used in order to allow seeing main page without authorization.
|
||||
If this feature is configured any request will be prohibited without authentication. In addition, configuration flag ``auth.allow_read_only`` can be used in order to allow read-only operations - reading index page and packages - without authorization.
|
||||
|
||||
For authenticated users it uses encrypted session cookies to store tokens; encryption key is generated each time at the start of the application. It also stores expiration time of the session inside.
|
||||
|
||||
|
@ -104,5 +104,4 @@ autodoc_member_order = "groupwise"
|
||||
|
||||
autodoc_default_options = {
|
||||
"no-undoc-members": True,
|
||||
"special-members": "__init__",
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Some groups can be specified for each architecture separately. E.g. if there are ``build`` and ``build:x86_64`` groups it will use the option from ``build:x86_64`` for the ``x86_64`` architecture and ``build`` for any other (architecture specific group has higher priority). In case if both groups are presented, architecture specific options will be merged into global ones overriding them.
|
||||
Some groups can be specified for each architecture separately. E.g. if there are ``build`` and ``build:x86_64`` groups it will use an option from ``build:x86_64`` for the ``x86_64`` architecture and ``build`` for any other (architecture specific group has higher priority). In case if both groups are presented, architecture specific options will be merged into global ones overriding them.
|
||||
|
||||
There are two variable types which have been added to default ones, they are paths and lists. List values will be read in the same way as shell does:
|
||||
|
||||
@ -26,9 +26,11 @@ Base configuration settings.
|
||||
|
||||
libalpm and AUR related configuration.
|
||||
|
||||
* ``database`` - path to pacman local database cache, string, required.
|
||||
* ``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).
|
||||
|
||||
``auth`` group
|
||||
--------------
|
||||
@ -36,12 +38,12 @@ libalpm and AUR related configuration.
|
||||
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``.
|
||||
* ``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.
|
||||
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service, integer, optional, default is 7 days.
|
||||
* ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used.
|
||||
* ``oauth_scopes`` - scopes list for OAuth2 provider, which will allow retrieving user email (which is used for checking user permissions), e.g. ``https://www.googleapis.com/auth/userinfo.email`` for ``GoogleClient`` or ``user:email`` for ``GithubClient``, space separated list of strings, required in case if ``oauth`` is used.
|
||||
* ``safe_build_status`` - allow requesting status page without authorization, boolean, required.
|
||||
* ``salt`` - password hash salt, string, required in case if authorization enabled (automatically generated by ``create-user`` subcommand).
|
||||
|
||||
Authorized users are stored inside internal database, if any of external provides are used the password field for non-service users must be empty.
|
||||
@ -56,7 +58,7 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
|
||||
* ``ignore_packages`` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional.
|
||||
* ``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 trigger classes (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 mention.
|
||||
|
||||
``repository`` group
|
||||
--------------------
|
||||
@ -75,12 +77,42 @@ Settings for signing packages or repository. Group name can refer to architectur
|
||||
* ``key`` - default PGP key, string, required. This key will also be used for database signing if enabled.
|
||||
* ``key_*`` settings - PGP key which will be used for specific packages, string, optional. For example, if there is ``key_yay`` option the specified key will be used for yay package and default key for others.
|
||||
|
||||
``remote-pull`` group
|
||||
---------------------
|
||||
|
||||
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
|
||||
|
||||
It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details.
|
||||
|
||||
* ``target`` - list of remote pull triggers to be used, space separated list of strings, optional, defaults to ``gitremote``. It must point to valid section (or to section with architecture), e.g. ``gitremote`` must point to either ``gitremote`` or ``gitremote:x86_64`` (the one with architecture has higher priority).
|
||||
|
||||
Remote pull trigger
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* ``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
|
||||
---------------------
|
||||
|
||||
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.
|
||||
|
||||
* ``target`` - list of remote push triggers to be used, space separated list of strings, optional, defaults to ``gitremote``. It must point to valid section (or to section with architecture), e.g. ``gitremote`` must point to either ``gitremote`` or ``gitremote:x86_64`` (the one with architecture has higher priority).
|
||||
|
||||
Remote push trigger
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* ``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
|
||||
----------------
|
||||
|
||||
Report generation settings.
|
||||
|
||||
* ``target`` - list of reports to be generated, space separated list of strings, required. It must point to valid section (or to section with architecture), e.g. ``somerandomname`` must point to existing section, ``email`` must point to one of ``email`` of ``email:x86_64`` (the one with architecture has higher priority).
|
||||
* ``target`` - list of reports to be generated, space separated list of strings, required. It must point to valid section (or to section with architecture), e.g. ``somerandomname`` must point to existing section, ``email`` must point to either ``email`` or ``email:x86_64`` (the one with architecture has higher priority).
|
||||
|
||||
Type will be read from several ways:
|
||||
|
||||
@ -93,6 +125,7 @@ Type will be read from several ways:
|
||||
|
||||
Section name must be either ``console`` (plus optional architecture name, e.g. ``console:x86_64``) or random name with ``type`` set.
|
||||
|
||||
* ``type`` - type of the report, string, optional, must be set to ``console`` if exists.
|
||||
* ``use_utf`` - use utf8 symbols in output if set and ascii otherwise, boolean, optional, default ``yes``.
|
||||
|
||||
``email`` type
|
||||
@ -120,9 +153,9 @@ Section name must be either ``email`` (plus optional architecture name, e.g. ``e
|
||||
Section name must be either ``html`` (plus optional architecture name, e.g. ``html:x86_64``) or random name with ``type`` set.
|
||||
|
||||
* ``type`` - type of the report, string, optional, must be set to ``html`` if exists.
|
||||
* ``path`` - path to html report file, string, required.
|
||||
* ``homepage`` - link to homepage, string, optional.
|
||||
* ``link_path`` - prefix for HTML links, string, required.
|
||||
* ``path`` - path to html report file, string, required.
|
||||
* ``template_path`` - path to Jinja2 template, string, required.
|
||||
|
||||
``telegram`` type
|
||||
@ -137,6 +170,7 @@ Section name must be either ``telegram`` (plus optional architecture name, e.g.
|
||||
* ``link_path`` - prefix for HTML links, string, required.
|
||||
* ``template_path`` - path to Jinja2 template, string, required.
|
||||
* ``template_type`` - ``parse_mode`` to be passed to telegram API, one of ``MarkdownV2``, ``HTML``, ``Markdown``, string, optional, default ``HTML``.
|
||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
||||
|
||||
``upload`` group
|
||||
----------------
|
||||
@ -166,6 +200,7 @@ This feature requires Github key creation (see below). Section name must be eith
|
||||
#. 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``.
|
||||
* ``username`` - Github authorization user, string, required. Basically the same as ``owner``.
|
||||
|
||||
``rsync`` type
|
||||
|
211
docs/faq.rst
211
docs/faq.rst
@ -1,16 +1,17 @@
|
||||
|
||||
FAQ
|
||||
===
|
||||
|
||||
General topics
|
||||
--------------
|
||||
|
||||
What is the purpose of the project?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
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 do I install it?
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
How to install ahriman
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TL;DR
|
||||
|
||||
@ -25,8 +26,8 @@ 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>`.
|
||||
|
||||
What does "architecture specific" mean? / How to configure for different architectures?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
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.
|
||||
|
||||
@ -46,8 +47,8 @@ states that default build command is ``extra-x86_64-build``. But if there is sec
|
||||
|
||||
the ``extra-i686-build`` command will be used for ``i686`` architecture.
|
||||
|
||||
How to use reporter/upload settings?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to generate build reports
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Normally you probably like to generate only one report for the specific type, e.g. only one email report. In order to do it you will need to have the following configuration:
|
||||
|
||||
@ -87,8 +88,8 @@ But for some cases you would like to have multiple different reports with the sa
|
||||
type = email
|
||||
...
|
||||
|
||||
Okay, I've installed ahriman, how do I add new package?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How do I add new package
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
@ -101,8 +102,8 @@ Okay, I've installed ahriman, how do I add new package?
|
||||
sudo -u ahriman ahriman package-add ahriman
|
||||
sudo -u ahriman ahriman repo-update
|
||||
|
||||
AUR is fine, but I would like to create package from local PKGBUILD
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to build package from local PKGBUILD
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TL;DR
|
||||
|
||||
@ -112,25 +113,82 @@ TL;DR
|
||||
|
||||
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.
|
||||
|
||||
But I just wanted to change PKGBUILD from AUR a bit!
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Well it is supported also.
|
||||
How to fetch PKGBUILDs from remote repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will need:
|
||||
|
||||
#.
|
||||
Append ``triggers`` option in ``build`` section with the following line:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[build]
|
||||
triggers = ahriman.core.gitremote.RemotePullTrigger
|
||||
|
||||
#.
|
||||
Configure trigger as following:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[remote-pull]
|
||||
target = gitremote
|
||||
|
||||
[gitremote]
|
||||
pull_url = https://github.com/username/repository
|
||||
|
||||
During the next application run it will fetch repository from the specified url and will try to find packages there which can be used as local sources.
|
||||
|
||||
How to push updated PKGBUILDs to remote repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For that purpose you'd need to use another trigger called ``RemotePushTrigger``. Configure it as following:
|
||||
|
||||
#.
|
||||
Append ``triggers`` option in ``build`` section with the trigger name:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[build]
|
||||
triggers = ahriman.core.gitremote.RemotePushTrigger
|
||||
|
||||
#.
|
||||
Configure trigger as following:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[remote-push]
|
||||
target = gitremote
|
||||
|
||||
[gitremote]
|
||||
push_url = https://github.com/username/repository
|
||||
|
||||
Unlike ``RemotePullTrigger`` trigger, the ``RemotePushTrigger`` more likely will require authorization. It is highly recommended to use application tokens for that instead of using your password (e.g. for Github you can generate tokens `here <https://github.com/settings/tokens>`_ with scope ``public_repo``). Authorization can be supplied by using authorization part of the url, e.g. ``https://key:token@github.com/username/repository``.
|
||||
|
||||
How to change PKGBUILDs before build
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Well it is supported also. The recommended way is to patch specific function, e.g. by running ``sudo -u ahriman ahriman patch-add ahriman version``. This command will prompt for new value of the PKGBUILD variable ``version``. You can also write it to file and read from it ``sudo -u ahriman ahriman patch-add ahriman version version.patch``.
|
||||
|
||||
Alternatively you can create full-diff patches, which are calculated by using ``git diff`` from current PKGBUILD master branch:
|
||||
|
||||
#. Clone sources from AUR.
|
||||
#. Make changes you would like to (e.g. edit ``PKGBUILD``, add external patches).
|
||||
#. Run ``sudo -u ahriman ahriman patch-add /path/to/local/directory/with/PKGBUILD``.
|
||||
#. Run ``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).
|
||||
|
||||
Hey, I would like to rebuild the official repository package
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to build package from official repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
So 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 pacmann -s repository
|
||||
sudo -u ahriman ahriman package-add pacman -s 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 configure timer for this.
|
||||
|
||||
Package build fails because it cannot validate PGP signature of source files
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -141,17 +199,17 @@ TL;DR
|
||||
|
||||
sudo -u ahriman ahriman key-import ...
|
||||
|
||||
How do I check if there are new commits for VCS packages?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to update VCS packages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Normally the service handles VCS packages correctly, but it requires additional dependencies:
|
||||
Normally the service handles VCS packages correctly, however it requires additional dependencies:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
pacman -S breezy darcs mercurial subversion
|
||||
|
||||
I would like to remove package because it is no longer needed/moved to official repositories
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to remove package
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
@ -161,8 +219,8 @@ Also, there is command ``repo-remove-unknown`` which checks packages in AUR and
|
||||
|
||||
Remove commands also remove any package files (patches, caches etc).
|
||||
|
||||
There is new major release of %library-name%, how do I rebuild packages?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to rebuild packages after library update
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TL;DR
|
||||
|
||||
@ -174,8 +232,8 @@ You can even rebuild the whole repository (which is particular useful in case if
|
||||
|
||||
However, note that you do not need to rebuild repository in case if you just changed signing option, just use ``repo-sign`` command instead.
|
||||
|
||||
Hmm, I have packages built, but how can I use it?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to install built packages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Add the following lines to your ``pacman.conf``:
|
||||
|
||||
@ -186,8 +244,8 @@ Add the following lines to your ``pacman.conf``:
|
||||
|
||||
(You might need to add ``SigLevel`` option according to the pacman documentation.)
|
||||
|
||||
I would like to serve the repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to serve repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Easy. For example, nginx configuration (without SSL) will look like:
|
||||
|
||||
@ -224,7 +282,7 @@ Example of the status page configuration is the following (status service is usi
|
||||
Docker image
|
||||
------------
|
||||
|
||||
We provide official images which can be found under ``arcan1s/ahriman`` repository. Docker image is being updated on each master commit 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.
|
||||
We provide official images which can be found under ``arcan1s/ahriman`` repository. 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.:
|
||||
|
||||
@ -238,7 +296,7 @@ The default action (in case if no arguments provided) is ``repo-update``. Basica
|
||||
|
||||
docker run -v /path/to/local/repo:/var/lib/ahriman -v /etc/ahriman.ini:/etc/ahriman.ini.d/10-overrides.ini arcan1s/ahriman:latest
|
||||
|
||||
By default, it runs ``repo-update``, but it can be overwritten to any other command you would like to, e.g.:
|
||||
The action can be specified during run, e.g.:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
@ -285,11 +343,22 @@ For every next container run use arguments ``-e AHRIMAN_PORT=8080 --net=host``,
|
||||
|
||||
docker run --privileged -e AHRIMAN_PORT=8080 --net=host -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
|
||||
|
||||
Daemon service
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
There is special subcommand which emulates systemd timer and will perform repository update periodically:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
docker run arcan1s/ahriman:latest daemon
|
||||
|
||||
This command uses same rules as ``repo-update``, thus, e.g. requires ``--privileged`` flag.
|
||||
|
||||
Remote synchronization
|
||||
----------------------
|
||||
|
||||
Wait I would like to use the repository from another server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to sync repository to another server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are several choices:
|
||||
|
||||
@ -318,8 +387,8 @@ There are several choices:
|
||||
|
||||
After that just add ``/srv/repo`` to the ``pacman.conf`` as usual. You can also upload to S3 (e.g. ``Server = https://s3.eu-central-1.amazonaws.com/repository/x86_64``) or to Github (e.g. ``Server = https://github.com/ahriman/repository/releases/download/x86_64``).
|
||||
|
||||
How do I configure S3?
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to sync to S3
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Install dependencies:
|
||||
@ -377,8 +446,8 @@ How do I configure S3?
|
||||
region = eu-central-1
|
||||
secret_key = ...
|
||||
|
||||
How do I configure Github?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to sync to Github releases
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Create a repository.
|
||||
@ -401,8 +470,8 @@ How do I configure Github?
|
||||
Reporting
|
||||
---------
|
||||
|
||||
I would like to get report to email
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to report by email
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Install dependencies:
|
||||
@ -428,8 +497,8 @@ I would like to get report to email
|
||||
sender = me@example.com
|
||||
user = me@example.com
|
||||
|
||||
I'm using synchronization to S3 and would like to generate index page
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to generate index page for S3
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Install dependencies:
|
||||
@ -452,8 +521,8 @@ I'm using synchronization to S3 and would like to generate index page
|
||||
|
||||
After these steps ``index.html`` file will be automatically synced to S3
|
||||
|
||||
I would like to get messages to my telegram account/channel
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to post build report to telegram
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
It still requires additional dependencies:
|
||||
@ -502,8 +571,8 @@ If you did everything fine you should receive the message with the next update.
|
||||
Web service
|
||||
-----------
|
||||
|
||||
Readme mentions web interface, how do I use it?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to setup web service
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Install dependencies:
|
||||
@ -523,8 +592,8 @@ Readme mentions web interface, how do I use it?
|
||||
#.
|
||||
Start the web service ``systemctl enable --now ahriman-web@x86_64``.
|
||||
|
||||
I would like to limit user access to the status page
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to enable basic authorization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Install dependencies 😊:
|
||||
@ -555,8 +624,8 @@ I would like to limit user access to the status page
|
||||
|
||||
#. Restart web service ``systemctl restart ahriman-web@x86_64``.
|
||||
|
||||
I would like to use OAuth
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to enable OAuth authorization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#.
|
||||
Create OAuth web application, download its ``client_id`` and ``client_secret``.
|
||||
@ -636,51 +705,55 @@ Other topics
|
||||
How does it differ from %another-manager%?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Short answer - I do not know.
|
||||
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, 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 code documentation.
|
||||
|
||||
`archrepo2 <https://github.com/lilydjwg/archrepo2>`_
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Don't know, haven't tried it. But it lacks of documentation at least.
|
||||
|
||||
* Web interface.
|
||||
* No synchronization and reporting.
|
||||
* ``ahriman`` has web interface.
|
||||
* ``archrepo2`` doesn't have synchronization and reporting.
|
||||
* ``archrepo2`` actively uses direct shell calls and ``yaourt`` components.
|
||||
* It has constantly running process instead of timer process (it is not pro or con).
|
||||
* ``archrepo2`` has constantly running process instead of timer process (it is not pro or con).
|
||||
|
||||
`repoctl <https://github.com/cassava/repoctl>`_
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
* Web interface.
|
||||
* No reporting.
|
||||
* Local packages and patches support.
|
||||
* Some actions are not fully automated (e.g. package update still requires manual intervention for the build itself).
|
||||
* ``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 vs external services (e.g. nginx) in ``ahriman``.
|
||||
* Ability to host repository from same command in ``repoctl`` vs external services (e.g. nginx) in ``ahriman``.
|
||||
|
||||
`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:
|
||||
|
||||
* Web interface.
|
||||
* Better reporting with template support.
|
||||
* Synchronization features (there was only ``rsync`` based).
|
||||
* Local packages and patches support.
|
||||
* No dependency management.
|
||||
* And so on.
|
||||
* ``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.
|
||||
|
||||
``repo-scripts`` also have bad architecture and bad quality code and uses out-of-dated ``yaourt`` and ``package-query``.
|
||||
...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.
|
||||
|
||||
I would like to check service logs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
How to check service logs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, the service writes logs to ``/dev/log`` which can be accessed by using ``journalctl`` command (logs are written to the journal of the user under which command is run).
|
||||
|
||||
@ -690,7 +763,7 @@ You can also edit configuration and forward logs to ``stderr``, just change ``ha
|
||||
|
||||
sed -i 's/handlers = syslog_handler/handlers = console_handler/g' /etc/ahriman.ini.d/logging.ini
|
||||
|
||||
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>`_.
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
@ -6,13 +6,14 @@ Wrapper for managing custom repository inspired by `repo-scripts <https://github
|
||||
Features
|
||||
--------
|
||||
|
||||
* Install-configure-forget manager for own repository.
|
||||
* Install-configure-forget manager for the very own repository.
|
||||
* Multi-architecture support.
|
||||
* VCS packages support.
|
||||
* Sign support with gpg (repository, package, per package settings).
|
||||
* Synchronization to remote services (rsync, s3 and github) and report generation (email, html, telegram) and even ability to write own extensions.
|
||||
* Dependency manager.
|
||||
* VCS packages support.
|
||||
* Official repository support.
|
||||
* Ability to patch AUR packages and even create package from local PKGBUILDs.
|
||||
* Sign support with gpg (repository, package, per package settings).
|
||||
* 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.
|
||||
|
||||
Contents
|
||||
@ -29,10 +30,3 @@ Contents
|
||||
advanced-usage
|
||||
triggers
|
||||
modules
|
||||
|
||||
Indices and tables
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -6,7 +6,7 @@ Initial setup
|
||||
#.
|
||||
Change settings if required, see :doc:`configuration reference <configuration>` for more details.
|
||||
#.
|
||||
TL;DR
|
||||
Perform initial setup:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
@ -25,21 +25,24 @@ Initial setup
|
||||
Configure build tools (it is required for correct dependency management system):
|
||||
|
||||
#.
|
||||
Create build command, e.g. ``ln -s /usr/bin/archbuild /usr/local/bin/ahriman-x86_64-build`` (you can choose any name for command, basically it should be ``{name}-{arch}-build``).
|
||||
#.
|
||||
Create configuration file, e.g. ``cp /usr/share/devtools/pacman-{extra,ahriman}.conf`` (same as previous ``pacman-{name}.conf``).
|
||||
#.
|
||||
Change configuration file, add your own repository, add multilib repository etc;
|
||||
#.
|
||||
Set ``build_command`` option to point to your command.
|
||||
#.
|
||||
Configure ``/etc/sudoers.d/ahriman`` to allow running command without a password.
|
||||
Create build command (you can choose any name for command, basically it should be ``{name}-{arch}-build``):
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
ln -s /usr/bin/archbuild /usr/local/bin/ahriman-x86_64-build
|
||||
|
||||
#.
|
||||
Create configuration file (same as previous ``pacman-{name}.conf``):
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cp /usr/share/devtools/pacman-{extra,ahriman}.conf
|
||||
|
||||
#.
|
||||
Change configuration file, add your own repository, add multilib repository etc:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
echo '[multilib]' | tee -a /usr/share/devtools/pacman-ahriman.conf
|
||||
echo 'Include = /etc/pacman.d/mirrorlist' | tee -a /usr/share/devtools/pacman-ahriman.conf
|
||||
|
||||
@ -47,13 +50,25 @@ Initial setup
|
||||
echo 'SigLevel = Optional TrustAll' | tee -a /usr/share/devtools/pacman-ahriman.conf
|
||||
echo 'Server = file:///var/lib/ahriman/repository/$arch' | tee -a /usr/share/devtools/pacman-ahriman.conf
|
||||
|
||||
#.
|
||||
Set ``build_command`` option to point to your command:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
echo '[build]' | tee -a /etc/ahriman.ini.d/build.ini
|
||||
echo 'build_command = ahriman-x86_64-build' | tee -a /etc/ahriman.ini.d/build.ini
|
||||
|
||||
#.
|
||||
Configure ``/etc/sudoers.d/ahriman`` to allow running command without a password:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
echo 'Cmnd_Alias CARCHBUILD_CMD = /usr/local/bin/ahriman-x86_64-build *' | tee -a /etc/sudoers.d/ahriman
|
||||
echo 'ahriman ALL=(ALL) NOPASSWD: CARCHBUILD_CMD' | tee -a /etc/sudoers.d/ahriman
|
||||
chmod 400 /etc/sudoers.d/ahriman
|
||||
|
||||
This command supports several arguments, kindly refer to its help message.
|
||||
|
||||
#.
|
||||
Start and enable ``ahriman@.timer`` via ``systemctl``:
|
||||
|
||||
|
@ -1,16 +1,49 @@
|
||||
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 two types of extensions - reporting and files uploading. Each extension must derive from the ``Trigger`` class and implement ``run`` method
|
||||
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:
|
||||
|
||||
* ``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.
|
||||
* ``on_stop`` - action which will be called right before the exit.
|
||||
|
||||
Note, it isn't required to implement all of those methods (or even one of them), however, it is highly recommended to avoid trigger actions in ``__init__`` method as it will be run on any application start (e.g. even if you are just searching in AUR).
|
||||
|
||||
Built-in triggers
|
||||
-----------------
|
||||
|
||||
For the configuration details and settings explanation kindly refer to the :doc:`documentation <configuration>`.
|
||||
|
||||
``ahriman.core.gitremote.RemotePullTrigger``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This trigger will be called before any action (``on_start``) and pulls remote PKGBUILD repository locally; after that it copies found PKGBUILDs from the cloned repository to the local cache. It is useful in case if you have patched PGKBUILDs (or even missing in AUR) which you would like to use for package building and, technically, just simplifies the local package building.
|
||||
|
||||
In order to update those packages you would need to clone your repository separately, make changes in PKGBUILD (e.g. bump version and update checksums), commit them and push back. On the next ahriman's repository update, it will pull changes you commited and will perform package update.
|
||||
|
||||
``ahriman.core.gitremote.RemotePushTrigger``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This trigger will be called right after build process (``on_result``). It will pick PKGBUILDs for the updated packages, pull them (together with any other files) and commit and push changes to remote repository. No real use cases, but the most of user repositories do it.
|
||||
|
||||
``ahriman.core.report.ReportTrigger``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Trigger which can be used for reporting. It implements ``on_result`` method and thus being called on each build update and generates report (e.g. html, telegram etc) according to the current settings.
|
||||
|
||||
``ahriman.core.upload.UploadTrigger``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This trigger takes build result (``on_result``) and performs syncing of the local packages to the remote mirror (e.g. S3 or just by rsync).
|
||||
|
||||
Trigger example
|
||||
---------------
|
||||
|
||||
Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`_, which provides easy HTTP API for triggers for integrations).
|
||||
Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`_, which provides easy HTTP API for integration triggers).gre
|
||||
|
||||
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``). It can be retrieved from the same application instance.
|
||||
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
|
||||
As it has been mentioned, our trigger must derive from specific class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -24,9 +57,7 @@ As it has been mentioned, our trigger must derive from specific class
|
||||
self.channel = configuration.get("slack", "channel")
|
||||
self.username = configuration.get("slack", "username")
|
||||
|
||||
By now we have class with all required variables.
|
||||
|
||||
Lets implement run method. Slack API requires positing data with specific payload by HTTP, thus
|
||||
By now we have class with all required variables. Lets implement run method. Slack API requires positing data with specific payload by HTTP, thus:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -39,7 +70,7 @@ Lets implement run method. Slack API requires positing data with specific payloa
|
||||
response = requests.post(slack_url, data={"payload": json.dumps(payload)})
|
||||
response.raise_for_status()
|
||||
|
||||
Obviously you can implement the specified method in class, but for guide purpose it has been done as separated method. Now we can merge this method into the class
|
||||
Obviously you can implement the specified method in class, but for guide purpose it has been done as separated method. Now we can merge this method into the class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -51,12 +82,12 @@ Obviously you can implement the specified method in class, but for guide purpose
|
||||
self.channel = configuration.get("slack", "channel")
|
||||
self.username = configuration.get("slack", "username")
|
||||
|
||||
def run(self, result, packages):
|
||||
notify(result, self.slack_url, channel, username)
|
||||
def on_result(self, result, packages):
|
||||
notify(result, self.slack_url, self.channel, self.username)
|
||||
|
||||
Setup the trigger
|
||||
-----------------
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
First, put the trigger in any path it can be exported, e.g. by packing the resource into python package (which will lead to import path as ``package.slack_reporter.SlackReporter``) or just put file somewhere it can be accessed by application (e.g. ``/usr/local/lib/slack_reporter.py.SlackReporter``.
|
||||
First, put the trigger in any path it can be exported, e.g. by packing the resource into python package (which will lead to import path as ``package.slack_reporter.SlackReporter``) or just put file somewhere it can be accessed by application (e.g. ``/usr/local/lib/slack_reporter.py.SlackReporter``).
|
||||
|
||||
After that run application as usual and receive notification in your slack channel.
|
||||
|
@ -1,13 +1,13 @@
|
||||
# Maintainer: Evgeniy Alekseev
|
||||
|
||||
pkgname='ahriman'
|
||||
pkgver=2.0.0rc10
|
||||
pkgver=2.3.0rc3
|
||||
pkgrel=1
|
||||
pkgdesc="ArcH Linux ReposItory MANager"
|
||||
pkgdesc="ArcH linux ReposItory MANager"
|
||||
arch=('any')
|
||||
url="https://github.com/arcan1s/ahriman"
|
||||
license=('GPL3')
|
||||
depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
|
||||
depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-setuptools' 'python-srcinfo')
|
||||
makedepends=('python-build' 'python-installer' 'python-wheel')
|
||||
optdepends=('breezy: -bzr packages support'
|
||||
'darcs: -darcs packages support'
|
||||
|
@ -1 +1 @@
|
||||
u ahriman 643 "ArcH Linux ReposItory MANager" /var/lib/ahriman
|
||||
u ahriman 643 "ArcH linux ReposItory MANager" /var/lib/ahriman
|
@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=ArcH Linux ReposItory MANager web server (%I architecture)
|
||||
Description=ArcH linux ReposItory MANager web server (%I architecture)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=ArcH Linux ReposItory MANager (%I architecture)
|
||||
Description=ArcH linux ReposItory MANager (%I architecture)
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/ahriman --architecture %i update
|
||||
ExecStart=/usr/bin/ahriman --architecture %i repo-update --refresh
|
||||
User=ahriman
|
||||
Group=ahriman
|
@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=ArcH Linux ReposItory MANager timer (%I architecture)
|
||||
Description=ArcH linux ReposItory MANager timer (%I architecture)
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
|
@ -5,22 +5,24 @@ database = /var/lib/ahriman/ahriman.db
|
||||
|
||||
[alpm]
|
||||
database = /var/lib/pacman
|
||||
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
|
||||
repositories = core extra community multilib
|
||||
root = /
|
||||
use_ahriman_cache = yes
|
||||
|
||||
[auth]
|
||||
target = disabled
|
||||
max_age = 604800
|
||||
oauth_provider = GoogleClient
|
||||
oauth_scopes = https://www.googleapis.com/auth/userinfo.email
|
||||
safe_build_status = yes
|
||||
allow_read_only = yes
|
||||
|
||||
[build]
|
||||
archbuild_flags =
|
||||
build_command = extra-x86_64-build
|
||||
ignore_packages =
|
||||
makechrootpkg_flags =
|
||||
makepkg_flags = --nocolor
|
||||
makepkg_flags = --nocolor --ignorearch
|
||||
triggers = ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger
|
||||
|
||||
[repository]
|
||||
@ -37,7 +39,6 @@ target = console
|
||||
use_utf = yes
|
||||
|
||||
[email]
|
||||
full_template_path = /usr/share/ahriman/templates/repo-index.jinja2
|
||||
no_empty_report = yes
|
||||
template_path = /usr/share/ahriman/templates/email-index.jinja2
|
||||
ssl = disabled
|
||||
|
@ -1,5 +1,5 @@
|
||||
[loggers]
|
||||
keys = root,build_details,database,http,stderr,boto3,botocore,nose,s3transfer
|
||||
keys = root,http,stderr,boto3,botocore,nose,s3transfer
|
||||
|
||||
[handlers]
|
||||
keys = console_handler,syslog_handler
|
||||
@ -20,11 +20,11 @@ formatter = syslog_format
|
||||
args = ("/dev/log",)
|
||||
|
||||
[formatter_generic_format]
|
||||
format = [%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s
|
||||
format = [%(levelname)s %(asctime)s] [%(threadName)s] [%(name)s]: %(message)s
|
||||
datefmt =
|
||||
|
||||
[formatter_syslog_format]
|
||||
format = [%(levelname)s] [%(name)s] [%(filename)s:%(lineno)d] [%(funcName)s]: %(message)s
|
||||
format = [%(levelname)s] [%(threadName)s] [%(name)s]: %(message)s
|
||||
datefmt =
|
||||
|
||||
[logger_root]
|
||||
@ -32,18 +32,6 @@ level = DEBUG
|
||||
handlers = syslog_handler
|
||||
qualname = root
|
||||
|
||||
[logger_build_details]
|
||||
level = DEBUG
|
||||
handlers = syslog_handler
|
||||
qualname = build_details
|
||||
propagate = 0
|
||||
|
||||
[logger_database]
|
||||
level = DEBUG
|
||||
handlers = syslog_handler
|
||||
qualname = database
|
||||
propagate = 0
|
||||
|
||||
[logger_http]
|
||||
level = DEBUG
|
||||
handlers = syslog_handler
|
||||
|
@ -14,28 +14,29 @@
|
||||
|
||||
<div class="container">
|
||||
<h1>ahriman
|
||||
{% if auth.authenticated %}
|
||||
<img src="https://img.shields.io/badge/version-{{ version }}-informational" alt="{{ version }}">
|
||||
<img src="https://img.shields.io/badge/repository-{{ repository | replace("-", "--") }}-informational" alt="{{ repository }}">
|
||||
<img src="https://img.shields.io/badge/architecture-{{ architecture }}-informational" alt="{{ architecture }}">
|
||||
<img src="https://img.shields.io/badge/service%20status-{{ service.status }}-{{ service.status_color }}" alt="{{ service.status }}" title="{{ service.timestamp }}">
|
||||
{% endif %}
|
||||
<img id="badge-version" src="https://img.shields.io/badge/version-unknown-informational" alt="unknown">
|
||||
<img id="badge-repository" src="https://img.shields.io/badge/repository-unknown-informational" alt="unknown">
|
||||
<img id="badge-architecture" src="https://img.shields.io/badge/architecture-unknown-informational" alt="unknown">
|
||||
<img id="badge-status" src="https://img.shields.io/badge/service%20status-unknown-inactive" alt="unknown">
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div id="toolbar">
|
||||
{% if not auth.enabled or auth.username is not none %}
|
||||
<button id="add" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addForm">
|
||||
<i class="fa fa-plus"></i> add
|
||||
<button id="add-btn" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-form" hidden>
|
||||
<i class="bi bi-plus"></i> add
|
||||
</button>
|
||||
<button id="update" class="btn btn-secondary" onclick="updatePackages()" disabled>
|
||||
<i class="fa fa-play"></i> update
|
||||
<button id="update-btn" class="btn btn-secondary" onclick="updatePackages()" hidden>
|
||||
<i class="bi bi-play"></i> update
|
||||
</button>
|
||||
<button id="remove" class="btn btn-danger" onclick="removePackages()" disabled>
|
||||
<i class="fa fa-trash"></i> remove
|
||||
<button id="remove-btn" class="btn btn-danger" onclick="removePackages()" disabled hidden>
|
||||
<i class="bi bi-trash"></i> remove
|
||||
</button>
|
||||
{% endif %}
|
||||
<button class="btn btn-secondary" onclick="reload()">
|
||||
<i class="bi bi-arrow-clockwise"></i> reload
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table id="packages" class="table table-striped table-hover"
|
||||
@ -53,42 +54,22 @@
|
||||
data-show-fullscreen="true"
|
||||
data-show-search-clear-button="true"
|
||||
data-sortable="true"
|
||||
data-sort-reset="true"
|
||||
data-sort-name="base"
|
||||
data-sort-order="asc"
|
||||
data-toggle="table"
|
||||
data-toolbar="#toolbar">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th data-checkbox="true"></th>
|
||||
<th data-sortable="true" data-switchable="false">package base</th>
|
||||
<th data-sortable="true">version</th>
|
||||
<th data-sortable="true">packages</th>
|
||||
<th data-sortable="true" data-visible="false">groups</th>
|
||||
<th data-sortable="true" data-visible="false">licenses</th>
|
||||
<th data-sortable="true">last update</th>
|
||||
<th data-sortable="true">status</th>
|
||||
<th data-sortable="true" data-switchable="false" data-field="base">package base</th>
|
||||
<th data-sortable="true" data-field="version">version</th>
|
||||
<th data-sortable="true" data-field="packages">packages</th>
|
||||
<th data-sortable="true" data-visible="false" data-field="groups">groups</th>
|
||||
<th data-sortable="true" data-visible="false" data-field="licenses">licenses</th>
|
||||
<th data-sortable="true" data-field="timestamp">last update</th>
|
||||
<th data-sortable="true" data-cell-style="statusFormat" data-field="status">status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% if auth.authenticated %}
|
||||
{% for package in packages %}
|
||||
<tr data-package-base="{{ package.base }}">
|
||||
<td data-checkbox="true"></td>
|
||||
<td>{% if package.web_url is not none %}<a href="{{ package.web_url }}" title="{{ package.base }}">{{ package.base }}</a>{% else %}{{ package.base }}{% endif %}</td>
|
||||
<td>{{ package.version }}</td>
|
||||
<td>{{ package.packages|join("<br>"|safe) }}</td>
|
||||
<td>{{ package.groups|join("<br>"|safe) }}</td>
|
||||
<td>{{ package.licenses|join("<br>"|safe) }}</td>
|
||||
<td>{{ package.timestamp }}</td>
|
||||
<td class="table-{{ package.status_color }}">{{ package.status }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="100%">In order to see statuses you must login first.</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -110,7 +91,7 @@
|
||||
{% if auth.username is none %}
|
||||
{{ auth.control|safe }}
|
||||
{% else %}
|
||||
<form action="/user-api/v1/logout" method="post">
|
||||
<form action="/api/v1/logout" method="post">
|
||||
<button class="btn btn-link" style="text-decoration: none">logout ({{ auth.username }})</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
@ -122,11 +103,14 @@
|
||||
{% include "build-status/login-modal.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
{% include "build-status/package-actions-modals.jinja2" %}
|
||||
|
||||
{% include "utils/bootstrap-scripts.jinja2" %}
|
||||
|
||||
{% include "build-status/package-actions-script.jinja2" %}
|
||||
{% include "build-status/failed-modal.jinja2" %}
|
||||
{% include "build-status/success-modal.jinja2" %}
|
||||
|
||||
{% include "build-status/package-add-modal.jinja2" %}
|
||||
|
||||
{% include "build-status/table.jinja2" %}
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
<div id="failed-form" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger">
|
||||
<h4 class="modal-title">failed</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Packages update has failed.</p>
|
||||
<p id="error-details"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const failedForm = $("#failed-form");
|
||||
const errorDetails = $("#error-details");
|
||||
failedForm.on("hidden.bs.modal", () => { reload(); });
|
||||
|
||||
function showFailure(details) {
|
||||
errorDetails.text(details);
|
||||
failedForm.modal("show");
|
||||
}
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<div id="loginForm" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form action="/user-api/v1/login" method="post">
|
||||
<form action="/api/v1/login" method="post">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">login</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
@ -26,4 +26,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,60 +0,0 @@
|
||||
<div id="addForm" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">add new packages</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label for="package" class="col-sm-2 col-form-label">package</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="package" type="text" list="knownPackages" class="form-control" placeholder="AUR package" name="package" required>
|
||||
<datalist id="knownPackages"></datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">close</button>
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="requestPackages()">request</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPackages()">add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="failedForm" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger">
|
||||
<h4 class="modal-title">failed</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Packages update has failed.</p>
|
||||
<p id="errorDetails"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="successForm" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-success">
|
||||
<h4 class="modal-title">success</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Packages update has been run.</p>
|
||||
<ul id="successDetails"></ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,95 +0,0 @@
|
||||
<script>
|
||||
const $remove = $("#remove");
|
||||
const $update = $("#update");
|
||||
|
||||
const $table = $("#packages");
|
||||
$table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table",
|
||||
function () {
|
||||
$remove.prop("disabled", !$table.bootstrapTable("getSelections").length);
|
||||
$update.prop("disabled", !$table.bootstrapTable("getSelections").length);
|
||||
})
|
||||
|
||||
const $successForm = $("#successForm");
|
||||
const $successDetails = $("#successDetails");
|
||||
$successForm.on("hidden.bs.modal", function() { window.location.reload(); });
|
||||
|
||||
const $failedForm = $("#failedForm");
|
||||
const $errorDetails = $("#errorDetails");
|
||||
$failedForm.on("hidden.bs.modal", function() { window.location.reload(); });
|
||||
|
||||
const $package = $("#package");
|
||||
const $knownPackages = $("#knownPackages");
|
||||
$package.keyup(function () {
|
||||
const $this = $(this);
|
||||
clearTimeout($this.data("timeout"));
|
||||
|
||||
$this.data("timeout", setTimeout($.proxy(function () {
|
||||
const $value = $package.val();
|
||||
|
||||
$.ajax({
|
||||
url: "/service-api/v1/search",
|
||||
data: {"for": $value},
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: function (resp) {
|
||||
const $options = resp.map(function (pkg) {
|
||||
const $option = document.createElement("option");
|
||||
$option.value = pkg.package;
|
||||
$option.innerText = `${pkg.package} (${pkg.description})`;
|
||||
return $option;
|
||||
});
|
||||
$knownPackages.empty().append($options);
|
||||
$this.focus();
|
||||
},
|
||||
})
|
||||
}, this), 500));
|
||||
})
|
||||
|
||||
function doPackageAction($uri, $packages) {
|
||||
if ($packages.length === 0)
|
||||
return;
|
||||
$.ajax({
|
||||
url: $uri,
|
||||
data: JSON.stringify({packages: $packages}),
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
success: function (_) {
|
||||
const $details = $packages.map(function (pkg) {
|
||||
const $li = document.createElement("li");
|
||||
$li.innerText = pkg;
|
||||
return $li;
|
||||
});
|
||||
$successDetails.empty().append($details);
|
||||
$successForm.modal("show");
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
$errorDetails.text(errorThrown);
|
||||
$failedForm.modal("show");
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function getSelection() {
|
||||
return $.map($table.bootstrapTable("getSelections"), function(row) {
|
||||
return row._data["package-base"];
|
||||
})
|
||||
}
|
||||
|
||||
function addPackages() {
|
||||
const $packages = [$package.val()]
|
||||
doPackageAction("/service-api/v1/add", $packages);
|
||||
}
|
||||
|
||||
function requestPackages() {
|
||||
const $packages = [$package.val()]
|
||||
doPackageAction("/service-api/v1/request", $packages);
|
||||
}
|
||||
|
||||
function removePackages() { doPackageAction("/service-api/v1/remove", getSelection()); }
|
||||
|
||||
function updatePackages() { doPackageAction("/service-api/v1/add", getSelection()); }
|
||||
|
||||
$(function () {
|
||||
$table.bootstrapTable("uncheckAll");
|
||||
})
|
||||
</script>
|
@ -0,0 +1,61 @@
|
||||
<div id="add-form" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">add new packages</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label for="package" class="col-sm-2 col-form-label">package</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="package-form" type="text" list="known-packages-dlist" autocomplete="off" class="form-control" placeholder="AUR package" name="package" required>
|
||||
<datalist id="known-packages-dlist"></datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">close</button>
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="requestPackages()">request</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPackages()">add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const packageInput = $("#package-form");
|
||||
const knownPackages = $("#known-packages-dlist");
|
||||
packageInput.keyup(() => {
|
||||
clearTimeout(packageInput.data("timeout"));
|
||||
packageInput.data("timeout", setTimeout($.proxy(() => {
|
||||
const value = packageInput.val();
|
||||
|
||||
$.ajax({
|
||||
url: "/api/v1/service/search",
|
||||
data: {"for": value},
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: response => {
|
||||
const options = response.map(pkg => {
|
||||
const option = document.createElement("option");
|
||||
option.value = pkg.package;
|
||||
option.innerText = `${pkg.package} (${pkg.description})`;
|
||||
return option;
|
||||
});
|
||||
knownPackages.empty().append(options);
|
||||
},
|
||||
})
|
||||
}, this), 500));
|
||||
});
|
||||
|
||||
function addPackages() {
|
||||
const packages = [packageInput.val()];
|
||||
doPackageAction("/api/v1/service/add", packages);
|
||||
}
|
||||
|
||||
function requestPackages() {
|
||||
const packages = [packageInput.val()];
|
||||
doPackageAction("/api/v1/service/request", packages);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,28 @@
|
||||
<div id="success-form" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-success">
|
||||
<h4 class="modal-title">success</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Packages update has been run.</p>
|
||||
<ul id="success-details"></ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const successForm = $("#success-form");
|
||||
const successDetails = $("#success-details");
|
||||
successForm.on("hidden.bs.modal", () => { reload(); });
|
||||
|
||||
function showSuccess(details) {
|
||||
successDetails.empty().append(details);
|
||||
successForm.modal("show");
|
||||
}
|
||||
</script>
|
157
package/share/ahriman/templates/build-status/table.jinja2
Normal file
157
package/share/ahriman/templates/build-status/table.jinja2
Normal file
@ -0,0 +1,157 @@
|
||||
<script>
|
||||
const addButton = $("#add-btn");
|
||||
const removeButton = $("#remove-btn");
|
||||
const updateButton = $("#update-btn");
|
||||
|
||||
const table = $("#packages");
|
||||
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table",
|
||||
() => {
|
||||
removeButton.prop("disabled", !table.bootstrapTable("getSelections").length);
|
||||
});
|
||||
|
||||
const architectureBadge = $("#badge-architecture");
|
||||
const repositoryBadge = $("#badge-repository");
|
||||
const statusBadge = $("#badge-status");
|
||||
const versionBadge = $("#badge-version");
|
||||
|
||||
function doPackageAction(uri, packages) {
|
||||
$.ajax({
|
||||
url: uri,
|
||||
data: JSON.stringify({packages: packages}),
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
success: _ => {
|
||||
const details = packages.map(pkg => {
|
||||
const li = document.createElement("li");
|
||||
li.innerText = pkg;
|
||||
return li;
|
||||
});
|
||||
showSuccess(details);
|
||||
},
|
||||
error: (jqXHR, _, errorThrown) => { showFailure(errorThrown); },
|
||||
});
|
||||
}
|
||||
|
||||
function getSelection() {
|
||||
return table.bootstrapTable("getSelections").map(row => { return row.id; });
|
||||
}
|
||||
|
||||
function removePackages() { doPackageAction("/api/v1/service/remove", getSelection()); }
|
||||
|
||||
function updatePackages() { doPackageAction("/api/v1/service/add", getSelection()); }
|
||||
|
||||
function hideControls(hidden) {
|
||||
addButton.attr("hidden", hidden);
|
||||
removeButton.attr("hidden", hidden);
|
||||
updateButton.attr("hidden", hidden);
|
||||
}
|
||||
|
||||
function reload() {
|
||||
table.bootstrapTable("showLoading");
|
||||
|
||||
$.ajax({
|
||||
url: "/api/v1/packages",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: response => {
|
||||
const extractListProperties = (description, property) => {
|
||||
return Object.values(description.packages)
|
||||
.map(pkg => { return pkg[property]; })
|
||||
.reduce((left, right) => { return left.concat(right); }, []);
|
||||
};
|
||||
const listToTable = data => {
|
||||
return Array.from(new Set(data))
|
||||
.sort()
|
||||
.map(entry => { return 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),
|
||||
version: safe(description.package.version),
|
||||
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(),
|
||||
status: description.status.status,
|
||||
};
|
||||
});
|
||||
|
||||
table.bootstrapTable("load", payload);
|
||||
table.bootstrapTable("uncheckAll");
|
||||
table.bootstrapTable("hideLoading");
|
||||
hideControls(false);
|
||||
},
|
||||
error: (jqXHR, _, errorThrown) => {
|
||||
if ((jqXHR.status === 401) || (jqXHR.status === 403)) {
|
||||
// authorization error
|
||||
const text = "In order to see statuses you must login first.";
|
||||
table.find("tr.unauthorized").remove();
|
||||
table.find("tbody").append(`<tr class="unauthorized"><td colspan="100%">${safe(text)}</td></tr>`);
|
||||
table.bootstrapTable("hideLoading");
|
||||
} else {
|
||||
// other errors
|
||||
showFailure(errorThrown);
|
||||
}
|
||||
hideControls(true);
|
||||
},
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: "/api/v1/status",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: response => {
|
||||
const badgeColor = status => {
|
||||
if (status === "pending") return "yellow";
|
||||
if (status === "building") return "yellow";
|
||||
if (status === "failed") return "critical";
|
||||
if (status === "success") return "success";
|
||||
return "inactive";
|
||||
};
|
||||
|
||||
architectureBadge
|
||||
.attr("src", `https://img.shields.io/badge/architecture-${response.architecture}-informational`)
|
||||
.attr("alt", response.architecture);
|
||||
repositoryBadge
|
||||
.attr("src", `https://img.shields.io/badge/repository-${response.repository.replace(/-/g, "--")}-informational`)
|
||||
.attr("alt", response.repository);
|
||||
statusBadge
|
||||
.attr("src", `https://img.shields.io/badge/service%20status-${response.status.status}-${badgeColor(response.status.status)}`)
|
||||
.attr("alt", response.status.status)
|
||||
.attr("title", `at ${new Date(1000 * response.status.timestamp).toISOString()}`);
|
||||
versionBadge
|
||||
.attr("src", `https://img.shields.io/badge/version-${response.version}-informational`)
|
||||
.attr("alt", response.version);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function safe(string) {
|
||||
return String(string)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function statusFormat(value) {
|
||||
const cellClass = status => {
|
||||
if (status === "pending") return "table-warning";
|
||||
if (status === "building") return "table-warning";
|
||||
if (status === "failed") return "table-danger";
|
||||
if (status === "success") return "table-success";
|
||||
return "table-secondary";
|
||||
};
|
||||
return {classes: cellClass(value)};
|
||||
}
|
||||
|
||||
$(() => {
|
||||
table.bootstrapTable({});
|
||||
reload();
|
||||
});
|
||||
</script>
|
@ -11,7 +11,7 @@
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h1>Arch Linux user repository</h1>
|
||||
<h1>Arch linux user repository</h1>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
@ -40,7 +40,8 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
||||
data-show-fullscreen="true"
|
||||
data-show-search-clear-button="true"
|
||||
data-sortable="true"
|
||||
data-sort-reset="true"
|
||||
data-sort-name="base"
|
||||
data-sort-order="asc"
|
||||
data-toggle="table">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
|
18
package/share/ahriman/templates/shell
Normal file
18
package/share/ahriman/templates/shell
Normal file
@ -0,0 +1,18 @@
|
||||
[00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[49;38;5;60m▄[39m [38;5;60m▄▄▄▄▄▄[48;5;60m█[49m▀[39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67;38;5;60m▄[48;5;60;38;5;103m▄[48;5;103;38;5;110m▄[48;5;60;38;5;103m▄[38;5;67m▄▄▄[48;5;67m██[38;5;60m▄▄▄[38;5;67m█[48;5;60m▄[49;38;5;60m▄[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m█[48;5;60;38;5;103m▄[48;5;103;38;5;110m▄[48;5;110m██[48;5;103;38;5;103m█[48;5;60;38;5;67m▄▄▄▄[48;5;67;38;5;60m▄[38;5;67m██[48;5;60m▄[48;5;67;38;5;60m▄[38;5;67m█[48;5;60m▄[49;38;5;60m▄[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67m▄[48;5;103;38;5;103m█[48;5;110;38;5;110m█[48;5;103;38;5;103m█[48;5;110;38;5;110m██[48;5;103;38;5;103m█[48;5;60;38;5;110m▄▄[38;5;103m▄[48;5;110;38;5;110m█[48;5;60;38;5;60m█[48;5;67;38;5;67m██[48;5;60m▄[48;5;67;38;5;60m▄[38;5;67m█[48;5;60m▄[49;38;5;60m▄[39m [38;5;60m▄[39m[00m
|
||||
[48;5;60;38;5;60m█[38;5;67m▄[48;5;103;38;5;60m▄[48;5;110;38;5;103m▄[38;5;110m█████████[48;5;60m▄▄[48;5;67;38;5;60m▄▄[49m▀[48;5;67m▄[38;5;67m█[48;5;60m▄[38;5;60m█[49m▀[39m[00m
|
||||
[48;5;60;38;5;60m█[49m▄▀[48;5;67m▄[48;5;103;38;5;103m█[48;5;110;38;5;110m███[38;5;232m▄[38;5;110m█[38;5;232m▄▄▄▄[38;5;110m███[48;5;103m▄[49;38;5;103m▄[39m [38;5;60m▀▀[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67m▄[48;5;60;38;5;67m▄▄[48;5;103;38;5;103m█[48;5;110;38;5;110m███[38;5;232m▄[48;5;232m█[48;5;188;38;5;188m█[48;5;231;38;5;231m██[48;5;232;38;5;232m██[48;5;110;38;5;110m███[48;5;103;38;5;103m█[49;39m [00m
|
||||
[38;5;60m▄▄[48;5;60;38;5;67m▄▄▄▄[49;38;5;60m▄▄▄[39m [38;5;60m▀[48;5;60m█[38;5;67m▄[48;5;103;38;5;103m█[48;5;110;38;5;110m████[48;5;232m▄[48;5;145;38;5;231m▄[48;5;232;38;5;145m▄[38;5;231m▄[38;5;232m█[38;5;231m▄[48;5;110;38;5;110m██[48;5;103;38;5;103m█[49m▄[39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m███[38;5;60m▄▄▄▄▄▄[48;5;60;38;5;67m▄▄[49;38;5;60m▄[39m [38;5;60m▀[39m [38;5;103m▀[48;5;110m▄[38;5;110m█████[48;5;188m▄▄[48;5;110m█[38;5;103m▄[38;5;110m██[48;5;103;38;5;103m█[49;39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m██[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m████[38;5;60m▄▄[48;5;60m█[48;5;67m▄[38;5;67m█[48;5;60;38;5;60m█[49;38;5;103m▄▄▄[39m [48;5;103;38;5;103m█[48;5;110;38;5;110m██[48;5;103m▄[48;5;110;38;5;103m▄▄▄▄▄[48;5;103m█[49m▀▀[39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m█[48;5;60;38;5;60m█[48;5;67;38;5;67m████[38;5;60m▄[49m▀[39m [48;5;60;38;5;103m▄[48;5;103;38;5;110m▄[48;5;110;38;5;179m▄[38;5;110m██[48;5;103m▄▄▄[48;5;110m████[48;5;103;38;5;103m█[49;39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m█[48;5;60m▄[48;5;67;38;5;60m▄[38;5;67m███[48;5;60;38;5;60m█[49;39m [48;5;103;38;5;103m█[48;5;185;38;5;110m▄[48;5;110m█[38;5;179m▄[48;5;179;38;5;110m▄[48;5;110m████████[48;5;103;38;5;103m█[49;39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m█[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m██[38;5;60m▄[49m▀[39m [38;5;103m▀[48;5;110m▄[38;5;110m████[38;5;103m▄[38;5;110m██[38;5;103m▄[38;5;110m██[38;5;103m▄[48;5;103;38;5;67m▄[49;39m [00m
|
||||
[48;5;60;38;5;60m█[48;5;67;38;5;67m██[48;5;60;38;5;60m█[48;5;67;38;5;67m███[48;5;60;38;5;60m█[49;39m [38;5;103m▄[48;5;103m█[48;5;110;38;5;110m██[38;5;103m▄[48;5;103;38;5;110m▄[38;5;67m▄[38;5;103m█[38;5;110m▄[48;5;110m█[48;5;103;38;5;103m█[48;5;110;38;5;110m█[48;5;67;38;5;67m█[49;39m [00m
|
||||
[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67;38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m███[38;5;60m▄[49m▀[39m [38;5;103m▄[48;5;103;38;5;110m▄[48;5;110m██[48;5;103;38;5;103m█[48;5;110;38;5;110m█[48;5;67;38;5;67m█[49;39m [48;5;103;38;5;103m█[48;5;110;38;5;110m██[48;5;103m▄[48;5;110;38;5;103m▄[48;5;67;38;5;110m▄[49;38;5;67m▄[39m [00m
|
||||
[38;5;60m▄▄▄[48;5;60m█[48;5;67;38;5;67m█[38;5;60m▄[48;5;60;38;5;67m▄[48;5;67m█[38;5;60m▄▄▄[49m▀[39m [48;5;103;38;5;103m█[48;5;110;38;5;110m███[48;5;103;38;5;103m█[48;5;110;38;5;67m▄[48;5;67m█[49;39m [48;5;103;38;5;103m█[48;5;110;38;5;110m███[48;5;103;38;5;103m█[48;5;110;38;5;67m▄[48;5;67m█[49;39m [00m
|
||||
[38;5;60m▀▀▀▀▀▀▀[39m [38;5;103m▀▀▀▀[39m [38;5;103m▀▀▀▀[39m [00m
|
@ -4,20 +4,9 @@
|
||||
|
||||
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
|
||||
<script>
|
||||
$("#packages").bootstrapTable({
|
||||
formatClearSearch: function () {
|
||||
return "Clear search";
|
||||
},
|
||||
formatSearch: function () {
|
||||
return "search";
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script src="https://kit.fontawesome.com/0d6d6d5226.js" crossorigin="anonymous"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
|
||||
|
||||
|
12
setup.py
12
setup.py
@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import find_packages, setup
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ setup(
|
||||
version=metadata["__version__"],
|
||||
zip_safe=False,
|
||||
|
||||
description="ArcH Linux ReposItory MANager",
|
||||
description="ArcH linux ReposItory MANager",
|
||||
|
||||
author="ahriman team",
|
||||
author_email="",
|
||||
@ -32,6 +32,7 @@ setup(
|
||||
"inflection",
|
||||
"passlib",
|
||||
"requests",
|
||||
"setuptools",
|
||||
"srcinfo",
|
||||
],
|
||||
setup_requires=[
|
||||
@ -66,12 +67,15 @@ setup(
|
||||
"package/share/ahriman/templates/build-status.jinja2",
|
||||
"package/share/ahriman/templates/email-index.jinja2",
|
||||
"package/share/ahriman/templates/repo-index.jinja2",
|
||||
"package/share/ahriman/templates/shell",
|
||||
"package/share/ahriman/templates/telegram-index.jinja2",
|
||||
]),
|
||||
("share/ahriman/templates/build-status", [
|
||||
"package/share/ahriman/templates/build-status/failed-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/login-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/package-actions-modals.jinja2",
|
||||
"package/share/ahriman/templates/build-status/package-actions-script.jinja2",
|
||||
"package/share/ahriman/templates/build-status/package-add-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/success-modal.jinja2",
|
||||
"package/share/ahriman/templates/build-status/table.jinja2",
|
||||
]),
|
||||
("share/ahriman/templates/static", [
|
||||
"package/share/ahriman/templates/static/favicon.ico",
|
||||
|
@ -17,6 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# pylint: disable=too-many-lines
|
||||
import argparse
|
||||
import sys
|
||||
import tempfile
|
||||
@ -26,6 +27,7 @@ from typing import List, TypeVar
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.application import handlers
|
||||
from ahriman.core.util import enum_values
|
||||
from ahriman.models.action import Action
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.package_source import PackageSource
|
||||
@ -63,7 +65,7 @@ def _parser() -> argparse.ArgumentParser:
|
||||
Returns:
|
||||
argparse.ArgumentParser: command line parser for the application
|
||||
"""
|
||||
parser = argparse.ArgumentParser(prog="ahriman", description="ArcH Linux ReposItory MANager",
|
||||
parser = argparse.ArgumentParser(prog="ahriman", description="ArcH linux ReposItory MANager",
|
||||
epilog="Argument list can also be read from file by using @ prefix.",
|
||||
fromfile_prefix_chars="@", formatter_class=_formatter)
|
||||
parser.add_argument("-a", "--architecture", help="target architectures (can be used multiple times)",
|
||||
@ -72,15 +74,17 @@ def _parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--force", help="force run, remove file lock", action="store_true")
|
||||
parser.add_argument("-l", "--lock", help="lock file", type=Path,
|
||||
default=Path(tempfile.gettempdir()) / "ahriman.lock")
|
||||
parser.add_argument("--no-report", help="force disable reporting to web service", action="store_true")
|
||||
parser.add_argument("--report", help="force enable or disable reporting to web service",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("-q", "--quiet", help="force disable any logging", action="store_true")
|
||||
parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user. Some actions might be unavailable",
|
||||
action="store_true")
|
||||
parser.add_argument("-v", "--version", action="version", version=version.__version__)
|
||||
parser.add_argument("-V", "--version", action="version", version=version.__version__)
|
||||
|
||||
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command", required=True)
|
||||
|
||||
_set_aur_search_parser(subparsers)
|
||||
_set_daemon_parser(subparsers)
|
||||
_set_help_parser(subparsers)
|
||||
_set_help_commands_unsafe_parser(subparsers)
|
||||
_set_key_import_parser(subparsers)
|
||||
@ -92,21 +96,26 @@ def _parser() -> argparse.ArgumentParser:
|
||||
_set_patch_add_parser(subparsers)
|
||||
_set_patch_list_parser(subparsers)
|
||||
_set_patch_remove_parser(subparsers)
|
||||
_set_patch_set_add_parser(subparsers)
|
||||
_set_repo_backup_parser(subparsers)
|
||||
_set_repo_check_parser(subparsers)
|
||||
_set_repo_clean_parser(subparsers)
|
||||
_set_repo_config_parser(subparsers)
|
||||
_set_repo_rebuild_parser(subparsers)
|
||||
_set_repo_remove_unknown_parser(subparsers)
|
||||
_set_repo_report_parser(subparsers)
|
||||
_set_repo_restore_parser(subparsers)
|
||||
_set_repo_setup_parser(subparsers)
|
||||
_set_repo_sign_parser(subparsers)
|
||||
_set_repo_status_update_parser(subparsers)
|
||||
_set_repo_sync_parser(subparsers)
|
||||
_set_repo_triggers_parser(subparsers)
|
||||
_set_repo_update_parser(subparsers)
|
||||
_set_shell_parser(subparsers)
|
||||
_set_user_add_parser(subparsers)
|
||||
_set_user_list_parser(subparsers)
|
||||
_set_user_remove_parser(subparsers)
|
||||
_set_version_parser(subparsers)
|
||||
_set_web_parser(subparsers)
|
||||
|
||||
return parser
|
||||
@ -124,14 +133,44 @@ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
parser = root.add_parser("aur-search", aliases=["search"], help="search for package",
|
||||
description="search for package in AUR using API", formatter_class=_formatter)
|
||||
parser.add_argument("search", help="search terms, can be specified multiple times, result will match all terms",
|
||||
parser.add_argument("search", help="search terms, can be specified multiple times, the result will match all terms",
|
||||
nargs="+")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
|
||||
parser.add_argument("--info", help="show additional package information",
|
||||
action=argparse.BooleanOptionalAction, default=False)
|
||||
parser.add_argument("--sort-by", help="sort field by this field. In case if two packages have the same value of "
|
||||
"the specified field, they will be always sorted by name",
|
||||
default="name", choices=sorted(handlers.Search.SORT_FIELDS))
|
||||
parser.set_defaults(handler=handlers.Search, architecture=[""], lock=None, no_report=True, quiet=True, unsafe=True)
|
||||
parser.set_defaults(handler=handlers.Search, architecture=[""], lock=None, report=False, quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for daemon subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("daemon", help="run application as daemon",
|
||||
description="start process which periodically will run update process",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
|
||||
parser.add_argument("--aur", help="enable or disable checking for AUR updates. Implies --no-vcs",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--manual", help="include or exclude manual updates",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||
"-yy to force refresh even if up to date",
|
||||
action="count", default=0)
|
||||
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
|
||||
return parser
|
||||
|
||||
|
||||
@ -149,7 +188,7 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
description="show help message for application or command and exit",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("command", help="show help message for specific command", nargs="?")
|
||||
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, no_report=True, quiet=True,
|
||||
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, quiet=True,
|
||||
unsafe=True, parser=_parser)
|
||||
return parser
|
||||
|
||||
@ -168,7 +207,7 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument
|
||||
description="list unsafe commands as defined in default args", formatter_class=_formatter)
|
||||
parser.add_argument("--command", help="instead of showing commands, just test command line for unsafe subcommand "
|
||||
"and return 0 in case if command is safe and 1 otherwise")
|
||||
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, no_report=True, quiet=True,
|
||||
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, report=False, quiet=True,
|
||||
unsafe=True, parser=_parser)
|
||||
return parser
|
||||
|
||||
@ -192,7 +231,7 @@ def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("--key-server", help="key server for key import", default="pgp.mit.edu")
|
||||
parser.add_argument("key", help="PGP key to import from public server")
|
||||
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, no_report=True)
|
||||
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, report=False)
|
||||
return parser
|
||||
|
||||
|
||||
@ -222,8 +261,11 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
|
||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||
"-yy to force refresh even if up to date",
|
||||
action="count", default=0)
|
||||
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
||||
type=PackageSource, choices=PackageSource, default=PackageSource.Auto)
|
||||
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
||||
parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true")
|
||||
parser.set_defaults(handler=handlers.Add)
|
||||
return parser
|
||||
@ -265,8 +307,8 @@ def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
|
||||
parser.add_argument("-s", "--status", help="filter packages by status",
|
||||
type=BuildStatusEnum, choices=BuildStatusEnum)
|
||||
parser.set_defaults(handler=handlers.Status, lock=None, no_report=True, quiet=True, unsafe=True)
|
||||
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
|
||||
parser.set_defaults(handler=handlers.Status, lock=None, report=False, quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
@ -285,8 +327,8 @@ def _set_package_status_remove_parser(root: SubParserAction) -> argparse.Argumen
|
||||
epilog="Please note that this subcommand does not remove the package itself, it just "
|
||||
"clears the status page.",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("package", help="remove specified packages", nargs="+")
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Remove, lock=None, no_report=True, quiet=True,
|
||||
parser.add_argument("package", help="remove specified packages from status page", nargs="+")
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Remove, lock=None, report=False, quiet=True,
|
||||
unsafe=True)
|
||||
return parser
|
||||
|
||||
@ -306,16 +348,16 @@ def _set_package_status_update_parser(root: SubParserAction) -> argparse.Argumen
|
||||
parser.add_argument("package", help="set status for specified packages. "
|
||||
"If no packages supplied, service status will be updated",
|
||||
nargs="*")
|
||||
parser.add_argument("-s", "--status", help="new status",
|
||||
type=BuildStatusEnum, choices=BuildStatusEnum, default=BuildStatusEnum.Success)
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, no_report=True, quiet=True,
|
||||
parser.add_argument("-s", "--status", help="new package build status",
|
||||
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, report=False, quiet=True,
|
||||
unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for new patch subcommand
|
||||
add parser for new single-function patch subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
@ -323,17 +365,19 @@ def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("patch-add", help="add patch set", description="create or update source patches",
|
||||
epilog="In order to add a patch set for the package you will need to clone "
|
||||
"the AUR package manually, add required changes (e.g. external patches, "
|
||||
"edit PKGBUILD) and run command, e.g. ``ahriman patch path/to/directory``. "
|
||||
"By default it tracks *.patch and *.diff files, but this behavior can be changed "
|
||||
"by using --track option",
|
||||
parser = root.add_parser("patch-add", help="add patch for PKGBUILD function",
|
||||
description="create or update patched PKGBUILD function or variable",
|
||||
epilog="Unlike ``patch-set-add``, this function allows to patch only one PKGBUILD "
|
||||
"function, e.g. typing ``ahriman patch-add ahriman pkgver`` it will change the "
|
||||
"``pkgver`` inside PKGBUILD, typing ``ahriman patch-add ahriman build()`` "
|
||||
"it will change ``build()`` function inside PKGBUILD",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("package", help="path to directory with changed files for patch addition/update")
|
||||
parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
|
||||
default=["*.diff", "*.patch"])
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, no_report=True)
|
||||
parser.add_argument("package", help="package base")
|
||||
parser.add_argument("variable", help="PKGBUILD variable or function name. If variable is a function, "
|
||||
"it must end with ()")
|
||||
parser.add_argument("patch", help="path to file which contains function or variable value. If not set, "
|
||||
"the value will be read from stdin", type=Path, nargs="?")
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False)
|
||||
return parser
|
||||
|
||||
|
||||
@ -351,7 +395,9 @@ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
description="list available patches for the package", formatter_class=_formatter)
|
||||
parser.add_argument("package", help="package base", nargs="?")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, no_report=True)
|
||||
parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
|
||||
action="append")
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, report=False)
|
||||
return parser
|
||||
|
||||
|
||||
@ -368,7 +414,36 @@ def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser = root.add_parser("patch-remove", help="remove patch set", description="remove patches for the package",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("package", help="package base")
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture=[""], lock=None, no_report=True)
|
||||
parser.add_argument("-v", "--variable", help="should be used for single-function patches in case if you wold like "
|
||||
"to remove only specified PKGBUILD variables. In case if not set, "
|
||||
"it will remove all patches related to the package",
|
||||
action="append")
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture=[""], lock=None, report=False)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_patch_set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for new full-diff patch subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("patch-set-add", help="add patch set", description="create or update source patches",
|
||||
epilog="In order to add a patch set for the package you will need to clone "
|
||||
"the AUR package manually, add required changes (e.g. external patches, "
|
||||
"edit PKGBUILD) and run command, e.g. ``ahriman patch-set-add path/to/directory``. "
|
||||
"By default it tracks *.patch and *.diff files, but this behavior can be changed "
|
||||
"by using --track option",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("package", help="path to directory with changed files for patch addition/update", type=Path)
|
||||
parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
|
||||
default=["*.diff", "*.patch"])
|
||||
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False,
|
||||
variable=None)
|
||||
return parser
|
||||
|
||||
|
||||
@ -383,9 +458,9 @@ def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("repo-backup", help="backup repository data",
|
||||
description="backup settings and database", formatter_class=_formatter)
|
||||
description="backup repository settings and database", formatter_class=_formatter)
|
||||
parser.add_argument("path", help="path of the output archive", type=Path)
|
||||
parser.set_defaults(handler=handlers.Backup, architecture=[""], lock=None, no_report=True, unsafe=True)
|
||||
parser.set_defaults(handler=handlers.Backup, architecture=[""], lock=None, report=False, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
@ -400,12 +475,16 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("repo-check", aliases=["check"], help="check for updates",
|
||||
description="check for packages updates. Same as update --dry-run --no-manual",
|
||||
description="check for packages updates. Same as repo-update --dry-run --no-manual",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("package", help="filter check by package base", nargs="*")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
||||
parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_local=False, no_manual=True)
|
||||
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||
"-yy to force refresh even if up to date",
|
||||
action="count", default=0)
|
||||
parser.set_defaults(handler=handlers.Update, dry_run=True, aur=True, local=True, manual=False)
|
||||
return parser
|
||||
|
||||
|
||||
@ -425,10 +504,15 @@ def _set_repo_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"you should not run this command manually. Also in case if you are going to clear "
|
||||
"the chroot directories you will need root privileges.",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("--cache", help="clear directory with package caches", action="store_true")
|
||||
parser.add_argument("--chroot", help="clear build chroot", action="store_true")
|
||||
parser.add_argument("--manual", help="clear manually added packages queue", action="store_true")
|
||||
parser.add_argument("--packages", help="clear directory with built packages", action="store_true")
|
||||
parser.add_argument("--cache", help="clear directory with package caches",
|
||||
action=argparse.BooleanOptionalAction, default=False)
|
||||
parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
|
||||
parser.add_argument("--manual", help="clear manually added packages queue",
|
||||
action=argparse.BooleanOptionalAction, default=False)
|
||||
parser.add_argument("--packages", help="clear directory with built packages",
|
||||
action=argparse.BooleanOptionalAction, default=False)
|
||||
parser.add_argument("--pacman", help="clear directory with pacman local database cache",
|
||||
action=argparse.BooleanOptionalAction, default=False)
|
||||
parser.set_defaults(handler=handlers.Clean, quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
@ -446,7 +530,7 @@ def _set_repo_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser = root.add_parser("repo-config", aliases=["config"], help="dump configuration",
|
||||
description="dump configuration for the specified architecture",
|
||||
formatter_class=_formatter)
|
||||
parser.set_defaults(handler=handlers.Dump, lock=None, no_report=True, quiet=True, unsafe=True)
|
||||
parser.set_defaults(handler=handlers.Dump, lock=None, report=False, quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
@ -462,13 +546,13 @@ def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
parser = root.add_parser("repo-rebuild", aliases=["rebuild"], help="rebuild repository",
|
||||
description="force rebuild whole repository", formatter_class=_formatter)
|
||||
parser.add_argument("--depends-on", help="only rebuild packages that depend on specified package", action="append")
|
||||
parser.add_argument("--depends-on", help="only rebuild packages that depend on specified packages", action="append")
|
||||
parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
|
||||
action="store_true")
|
||||
parser.add_argument("--from-database",
|
||||
help="read packages from database instead of filesystem. This feature in particular is "
|
||||
"required in case if you would like to restore repository from another repository "
|
||||
"instance. Note however that in order to restore packages you need to have original "
|
||||
"instance. Note, however, that in order to restore packages you need to have original "
|
||||
"ahriman instance run with web service and have run repo-update at least once.",
|
||||
action="store_true")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
@ -490,11 +574,28 @@ def _set_repo_remove_unknown_parser(root: SubParserAction) -> argparse.ArgumentP
|
||||
description="remove packages which are missing in AUR and do not have local PKGBUILDs",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("--dry-run", help="just perform check for packages without removal", action="store_true")
|
||||
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
|
||||
parser.set_defaults(handler=handlers.RemoveUnknown)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_repo_report_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for report subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("repo-report", aliases=["report"], help="generate report",
|
||||
description="generate repository report according to current settings",
|
||||
epilog="Create and/or update repository report as configured.",
|
||||
formatter_class=_formatter)
|
||||
parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.report.ReportTrigger"])
|
||||
return parser
|
||||
|
||||
|
||||
def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for repository restore subcommand
|
||||
@ -509,7 +610,7 @@ def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
description="restore settings and database", formatter_class=_formatter)
|
||||
parser.add_argument("path", help="path of the input archive", type=Path)
|
||||
parser.add_argument("-o", "--output", help="root path of the extracted files", type=Path, default=Path("/"))
|
||||
parser.set_defaults(handler=handlers.Restore, architecture=[""], lock=None, no_report=True, unsafe=True)
|
||||
parser.set_defaults(handler=handlers.Restore, architecture=[""], lock=None, report=False, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
@ -531,14 +632,15 @@ def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser.add_argument("--build-command", help="build command prefix", default="ahriman")
|
||||
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
|
||||
type=Path, default=Path("/usr/share/devtools/pacman-extra.conf"))
|
||||
parser.add_argument("--no-multilib", help="do not add multilib repository", action="store_true")
|
||||
parser.add_argument("--multilib", help="add or do not multilib repository",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--packager", help="packager name and email", required=True)
|
||||
parser.add_argument("--repository", help="repository name", required=True)
|
||||
parser.add_argument("--sign-key", help="sign key id")
|
||||
parser.add_argument("--sign-target", help="sign options", action="append",
|
||||
type=SignSettings.from_option, choices=SignSettings)
|
||||
type=SignSettings.from_option, choices=enum_values(SignSettings))
|
||||
parser.add_argument("--web-port", help="port of the web service", type=int)
|
||||
parser.set_defaults(handler=handlers.Setup, lock=None, no_report=True, quiet=True, unsafe=True)
|
||||
parser.set_defaults(handler=handlers.Setup, lock=None, report=False, quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
@ -574,15 +676,33 @@ def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentPa
|
||||
parser = root.add_parser("repo-status-update", help="update repository status",
|
||||
description="update repository status on the status page", formatter_class=_formatter)
|
||||
parser.add_argument("-s", "--status", help="new status",
|
||||
type=BuildStatusEnum, choices=BuildStatusEnum, default=BuildStatusEnum.Success)
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, no_report=True, package=[],
|
||||
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
|
||||
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, report=False, package=[],
|
||||
quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_repo_sync_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for repository sync subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("repo-sync", aliases=["sync"], help="sync repository",
|
||||
description="sync repository files to remote server according to current settings",
|
||||
epilog="Synchronize the repository to remote services as configured.",
|
||||
formatter_class=_formatter)
|
||||
parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.upload.UploadTrigger"])
|
||||
return parser
|
||||
|
||||
|
||||
def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for repository sync subcommand
|
||||
add parser for repository triggers subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
@ -593,6 +713,8 @@ def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser = root.add_parser("repo-triggers", help="run triggers",
|
||||
description="run triggers on empty build result as configured by settings",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("trigger", help="instead of running all triggers as set by configuration, just process "
|
||||
"specified ones oin order of metion", nargs="*")
|
||||
parser.set_defaults(handler=handlers.Triggers)
|
||||
return parser
|
||||
|
||||
@ -613,14 +735,40 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser.add_argument("package", help="filter check by package base", nargs="*")
|
||||
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.add_argument("--no-aur", help="do not check for AUR updates. Implies --no-vcs", action="store_true")
|
||||
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
|
||||
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
|
||||
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
|
||||
parser.add_argument("--aur", help="enable or disable checking for AUR updates. Implies --no-vcs",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--manual", help="include or exclude manual updates",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("--vcs", help="enable or disable checking of VCS packages",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||
"-yy to force refresh even if up to date",
|
||||
action="count", default=0)
|
||||
parser.set_defaults(handler=handlers.Update)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for shell subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("shell", help="envoke python shell",
|
||||
description="drop into python shell while having created application",
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
|
||||
parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
|
||||
parser.set_defaults(handler=handlers.Shell, lock=None, report=False)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for create user subcommand
|
||||
@ -640,9 +788,9 @@ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser.add_argument("-p", "--password", help="user password. Blank password will be treated as empty password, "
|
||||
"which is in particular must be used for OAuth2 authorization type.")
|
||||
parser.add_argument("-r", "--role", help="user access level",
|
||||
type=UserAccess, choices=UserAccess, default=UserAccess.Read)
|
||||
type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
|
||||
parser.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
|
||||
parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, no_report=True,
|
||||
parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, report=False,
|
||||
quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
@ -662,8 +810,8 @@ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("username", help="filter users by username", nargs="?")
|
||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||
parser.add_argument("-r", "--role", help="filter users by role", type=UserAccess, choices=UserAccess)
|
||||
parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, no_report=True, # nosec
|
||||
parser.add_argument("-r", "--role", help="filter users by role", type=UserAccess, choices=enum_values(UserAccess))
|
||||
parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, report=False, # nosec
|
||||
password="", quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
@ -683,11 +831,28 @@ def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
formatter_class=_formatter)
|
||||
parser.add_argument("username", help="username for web service")
|
||||
parser.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
|
||||
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, no_report=True, # nosec
|
||||
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False, # nosec
|
||||
password="", quiet=True, unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for version subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("version", help="application version",
|
||||
description="print application and its dependencies versions", formatter_class=_formatter)
|
||||
parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, report=False, quiet=True,
|
||||
unsafe=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for web subcommand
|
||||
@ -699,7 +864,7 @@ def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("web", help="web server", description="start web server", formatter_class=_formatter)
|
||||
parser.set_defaults(handler=handlers.Web, lock=None, no_report=True, parser=_parser)
|
||||
parser.set_defaults(handler=handlers.Web, lock=None, report=False, parser=_parser)
|
||||
return parser
|
||||
|
||||
|
||||
|
@ -36,12 +36,12 @@ class Application(ApplicationPackages, ApplicationRepository):
|
||||
>>> from ahriman.models.package_source import PackageSource
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> application = Application("x86_64", configuration, no_report=False, unsafe=False)
|
||||
>>> application = Application("x86_64", configuration, report=True, unsafe=False)
|
||||
>>> # add packages to build queue
|
||||
>>> application.add(["ahriman"], PackageSource.AUR, without_dependencies=False)
|
||||
>>>
|
||||
>>> # check for updates
|
||||
>>> updates = application.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
|
||||
>>> updates = application.updates([], aur=True, local=True, manual=True, vcs=True, log_fn=print)
|
||||
>>> # updates for specified packages
|
||||
>>> application.update(updates)
|
||||
|
||||
@ -49,15 +49,6 @@ class Application(ApplicationPackages, ApplicationRepository):
|
||||
be used instead.
|
||||
"""
|
||||
|
||||
def _finalize(self, result: Result) -> None:
|
||||
"""
|
||||
generate report and sync to remote server
|
||||
|
||||
Args:
|
||||
result(Result): build result
|
||||
"""
|
||||
self.repository.process_triggers(result)
|
||||
|
||||
def _known_packages(self) -> Set[str]:
|
||||
"""
|
||||
load packages from repository and pacman repositories
|
||||
@ -71,5 +62,28 @@ class Application(ApplicationPackages, ApplicationRepository):
|
||||
for package, properties in base.packages.items():
|
||||
known_packages.add(package)
|
||||
known_packages.update(properties.provides)
|
||||
known_packages.update(self.repository.pacman.all_packages())
|
||||
known_packages.update(self.repository.pacman.packages())
|
||||
return known_packages
|
||||
|
||||
def on_result(self, result: Result) -> None:
|
||||
"""
|
||||
generate report and sync to remote server
|
||||
|
||||
Args:
|
||||
result(Result): build result
|
||||
"""
|
||||
packages = self.repository.packages()
|
||||
self.repository.triggers.on_result(result, packages)
|
||||
|
||||
def on_start(self) -> None:
|
||||
"""
|
||||
run triggers on start of the application
|
||||
"""
|
||||
self.repository.triggers.on_start()
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""
|
||||
run triggers on stop of the application. Note, however, that in most cases this method should not be called
|
||||
directly as it will be called after on_start action
|
||||
"""
|
||||
self.repository.triggers.on_stop()
|
||||
|
@ -21,11 +21,12 @@ import requests
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Any, Iterable, Set
|
||||
|
||||
from ahriman.application.application.application_properties import ApplicationProperties
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
from ahriman.core.util import package_like, tmpdir
|
||||
from ahriman.core.util import package_like
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.result import Result
|
||||
@ -36,18 +37,6 @@ class ApplicationPackages(ApplicationProperties):
|
||||
package control class
|
||||
"""
|
||||
|
||||
def _finalize(self, result: Result) -> None:
|
||||
"""
|
||||
generate report and sync to remote server
|
||||
|
||||
Args:
|
||||
result(Result): build result
|
||||
|
||||
Raises:
|
||||
NotImplementedError: not implemented method
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _known_packages(self) -> Set[str]:
|
||||
"""
|
||||
load packages from repository and pacman repositories
|
||||
@ -60,6 +49,18 @@ class ApplicationPackages(ApplicationProperties):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def on_result(self, result: Result) -> None:
|
||||
"""
|
||||
generate report and sync to remote server
|
||||
|
||||
Args:
|
||||
result(Result): build result
|
||||
|
||||
Raises:
|
||||
NotImplementedError: not implemented method
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _add_archive(self, source: str, *_: Any) -> None:
|
||||
"""
|
||||
add package from archive
|
||||
@ -85,7 +86,7 @@ class ApplicationPackages(ApplicationProperties):
|
||||
self.database.build_queue_insert(package)
|
||||
self.database.remote_update(package)
|
||||
|
||||
with tmpdir() as local_dir:
|
||||
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (local_dir := Path(dir_name)):
|
||||
Sources.load(local_dir, package, self.database.patches_get(package.base), self.repository.paths)
|
||||
self._process_dependencies(local_dir, known_packages, without_dependencies)
|
||||
|
||||
@ -127,7 +128,7 @@ class ApplicationPackages(ApplicationProperties):
|
||||
source(str): remote URL of the package archive
|
||||
"""
|
||||
dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
|
||||
response = requests.get(source, stream=True)
|
||||
response = requests.get(source, stream=True, timeout=None) # timeout=None to suppress pylint warns
|
||||
response.raise_for_status()
|
||||
|
||||
with dst.open("wb") as local_file:
|
||||
@ -185,4 +186,4 @@ class ApplicationPackages(ApplicationProperties):
|
||||
names(Iterable[str]): list of packages (either base or name) to remove
|
||||
"""
|
||||
self.repository.process_remove(names)
|
||||
self._finalize(Result())
|
||||
self.on_result(Result())
|
||||
|
@ -17,14 +17,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database import SQLite
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.core.repository import Repository
|
||||
|
||||
|
||||
class ApplicationProperties:
|
||||
class ApplicationProperties(LazyLogging):
|
||||
"""
|
||||
application base properties class
|
||||
|
||||
@ -32,22 +31,23 @@ class ApplicationProperties:
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
database(SQLite): database instance
|
||||
logger(logging.Logger): application logger
|
||||
repository(Repository): repository instance
|
||||
"""
|
||||
|
||||
def __init__(self, architecture: str, configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def __init__(self, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
|
||||
"""
|
||||
default constructor
|
||||
|
||||
Args:
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
refresh_pacman_database(int): pacman database syncronization level, ``0`` is disabled
|
||||
"""
|
||||
self.logger = logging.getLogger("root")
|
||||
self.configuration = configuration
|
||||
self.architecture = architecture
|
||||
self.database = SQLite.load(configuration)
|
||||
self.repository = Repository(architecture, configuration, self.database, no_report, unsafe)
|
||||
self.repository = Repository(architecture, configuration, self.database,
|
||||
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)
|
||||
|
@ -35,7 +35,7 @@ class ApplicationRepository(ApplicationProperties):
|
||||
repository control class
|
||||
"""
|
||||
|
||||
def _finalize(self, result: Result) -> None:
|
||||
def on_result(self, result: Result) -> None:
|
||||
"""
|
||||
generate report and sync to remote server
|
||||
|
||||
@ -47,7 +47,7 @@ class ApplicationRepository(ApplicationProperties):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def clean(self, cache: bool, chroot: bool, manual: bool, packages: bool) -> None:
|
||||
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
|
||||
"""
|
||||
run all clean methods. Warning: some functions might not be available under non-root
|
||||
|
||||
@ -56,6 +56,7 @@ class ApplicationRepository(ApplicationProperties):
|
||||
chroot(bool): clear build chroot
|
||||
manual(bool): clear directory with manually added packages
|
||||
packages(bool): clear directory with built packages
|
||||
pacman(bool): clear directory with pacman databases
|
||||
"""
|
||||
if cache:
|
||||
self.repository.clear_cache()
|
||||
@ -65,6 +66,8 @@ class ApplicationRepository(ApplicationProperties):
|
||||
self.repository.clear_queue()
|
||||
if packages:
|
||||
self.repository.clear_packages()
|
||||
if pacman:
|
||||
self.repository.clear_pacman()
|
||||
|
||||
def sign(self, packages: Iterable[str]) -> None:
|
||||
"""
|
||||
@ -89,7 +92,7 @@ class ApplicationRepository(ApplicationProperties):
|
||||
self.update([])
|
||||
# sign repository database if set
|
||||
self.repository.sign.process_sign_repository(self.repository.repo.repo_path)
|
||||
self._finalize(Result())
|
||||
self.on_result(Result())
|
||||
|
||||
def unknown(self) -> List[str]:
|
||||
"""
|
||||
@ -139,7 +142,7 @@ class ApplicationRepository(ApplicationProperties):
|
||||
if not paths:
|
||||
return # don't need to process if no update supplied
|
||||
update_result = self.repository.process_update(paths)
|
||||
self._finalize(result.merge(update_result))
|
||||
self.on_result(result.merge(update_result))
|
||||
|
||||
# process built packages
|
||||
build_result = Result()
|
||||
@ -156,17 +159,17 @@ class ApplicationRepository(ApplicationProperties):
|
||||
|
||||
return build_result
|
||||
|
||||
def updates(self, filter_packages: Iterable[str], no_aur: bool, no_local: bool, no_manual: bool, no_vcs: bool,
|
||||
log_fn: Callable[[str], None]) -> List[Package]:
|
||||
def updates(self, filter_packages: Iterable[str], *,
|
||||
aur: bool, local: bool, manual: bool, vcs: bool, log_fn: Callable[[str], None]) -> List[Package]:
|
||||
"""
|
||||
get list of packages to run update process
|
||||
|
||||
Args:
|
||||
filter_packages(Iterable[str]): do not check every package just specified in the list
|
||||
no_aur(bool): do not check for aur updates
|
||||
no_local(bool): do not check local packages for updates
|
||||
no_manual(bool): do not check for manual updates
|
||||
no_vcs(bool): do not check VCS packages
|
||||
aur(bool): enable or disable checking for AUR updates
|
||||
local(bool): enable or disable checking of local packages for updates
|
||||
manual(bool): include or exclude manual updates
|
||||
vcs(bool): enable or disable checking of VCS packages
|
||||
log_fn(Callable[[str], None]): logger function to log updates
|
||||
|
||||
Returns:
|
||||
@ -174,11 +177,11 @@ class ApplicationRepository(ApplicationProperties):
|
||||
"""
|
||||
updates = {}
|
||||
|
||||
if not no_aur:
|
||||
updates.update({package.base: package for package in self.repository.updates_aur(filter_packages, no_vcs)})
|
||||
if not no_local:
|
||||
if aur:
|
||||
updates.update({package.base: package for package in self.repository.updates_aur(filter_packages, vcs=vcs)})
|
||||
if local:
|
||||
updates.update({package.base: package for package in self.repository.updates_local()})
|
||||
if not no_manual:
|
||||
if manual:
|
||||
updates.update({package.base: package for package in self.repository.updates_manual()})
|
||||
|
||||
local_versions = {package.base: package.version for package in self.repository.packages()}
|
||||
|
@ -22,6 +22,7 @@ from ahriman.application.handlers.handler import Handler
|
||||
from ahriman.application.handlers.add import Add
|
||||
from ahriman.application.handlers.backup import Backup
|
||||
from ahriman.application.handlers.clean import Clean
|
||||
from ahriman.application.handlers.daemon import Daemon
|
||||
from ahriman.application.handlers.dump import Dump
|
||||
from ahriman.application.handlers.help import Help
|
||||
from ahriman.application.handlers.key_import import KeyImport
|
||||
@ -32,6 +33,7 @@ from ahriman.application.handlers.remove_unknown import RemoveUnknown
|
||||
from ahriman.application.handlers.restore import Restore
|
||||
from ahriman.application.handlers.search import Search
|
||||
from ahriman.application.handlers.setup import Setup
|
||||
from ahriman.application.handlers.shell import Shell
|
||||
from ahriman.application.handlers.sign import Sign
|
||||
from ahriman.application.handlers.status import Status
|
||||
from ahriman.application.handlers.status_update import StatusUpdate
|
||||
@ -39,4 +41,5 @@ from ahriman.application.handlers.triggers import Triggers
|
||||
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
|
||||
from ahriman.application.handlers.update import Update
|
||||
from ahriman.application.handlers.users import Users
|
||||
from ahriman.application.handlers.versions import Versions
|
||||
from ahriman.application.handlers.web import Web
|
||||
|
@ -32,8 +32,8 @@ class Add(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -41,14 +41,17 @@ class Add(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
application = Application(architecture, configuration,
|
||||
report=report, unsafe=unsafe, refresh_pacman_database=args.refresh)
|
||||
application.on_start()
|
||||
application.add(args.package, args.source, args.without_dependencies)
|
||||
if not args.now:
|
||||
return
|
||||
|
||||
packages = application.updates(args.package, True, True, False, True, application.logger.info)
|
||||
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False,
|
||||
log_fn=application.logger.info)
|
||||
result = application.update(packages)
|
||||
Add.check_if_empty(args.exit_code, result.is_empty)
|
||||
|
@ -37,8 +37,8 @@ class Backup(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -46,7 +46,7 @@ class Backup(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
backup_paths = Backup.get_paths(configuration)
|
||||
|
@ -32,8 +32,8 @@ class Clean(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -41,8 +41,10 @@ class Clean(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
Application(architecture, configuration, no_report, unsafe).clean(
|
||||
args.cache, args.chroot, args.manual, args.packages)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
application.on_start()
|
||||
application.clean(cache=args.cache, chroot=args.chroot, manual=args.manual, packages=args.packages,
|
||||
pacman=args.pacman)
|
||||
|
52
src/ahriman/application/handlers/daemon.py
Normal file
52
src/ahriman/application/handlers/daemon.py
Normal file
@ -0,0 +1,52 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
import threading
|
||||
|
||||
from typing import Type
|
||||
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
class Daemon(Handler):
|
||||
"""
|
||||
daemon packages handler
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
from ahriman.application.handlers import Update
|
||||
Update.run(args, architecture, configuration, report=report, unsafe=unsafe)
|
||||
timer = threading.Timer(args.interval, Daemon.run, args=[args, architecture, configuration],
|
||||
kwargs={"report": report, "unsafe": unsafe})
|
||||
timer.start()
|
||||
timer.join()
|
@ -34,8 +34,8 @@ class Dump(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -43,7 +43,7 @@ class Dump(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
dump = configuration.dump()
|
||||
|
@ -27,7 +27,7 @@ from typing import List, Type
|
||||
|
||||
from ahriman.application.lock import Lock
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import ExitCode, MissingArchitecture, MultipleArchitectures
|
||||
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ class Handler:
|
||||
if not cls.ALLOW_AUTO_ARCHITECTURE_RUN and args.architecture is None:
|
||||
# for some parsers (e.g. config) we need to run with specific architecture
|
||||
# for those cases architecture must be set explicitly
|
||||
raise MissingArchitecture(args.command)
|
||||
raise MissingArchitectureError(args.command)
|
||||
if args.architecture: # architecture is specified explicitly
|
||||
return sorted(set(args.architecture))
|
||||
|
||||
@ -78,7 +78,7 @@ class Handler:
|
||||
architectures = RepositoryPaths.known_architectures(root)
|
||||
|
||||
if not architectures: # well we did not find anything
|
||||
raise MissingArchitecture(args.command)
|
||||
raise MissingArchitectureError(args.command)
|
||||
return sorted(architectures)
|
||||
|
||||
@classmethod
|
||||
@ -96,7 +96,7 @@ class Handler:
|
||||
try:
|
||||
configuration = Configuration.from_path(args.configuration, architecture, args.quiet)
|
||||
with Lock(args, architecture, configuration):
|
||||
cls.run(args, architecture, configuration, args.no_report, args.unsafe)
|
||||
cls.run(args, architecture, configuration, report=args.report, unsafe=args.unsafe)
|
||||
return True
|
||||
except ExitCode:
|
||||
return False
|
||||
@ -124,7 +124,7 @@ class Handler:
|
||||
# actually we do not have to spawn another process if it is single-process application, do we?
|
||||
if len(architectures) > 1:
|
||||
if not cls.ALLOW_MULTI_ARCHITECTURE_RUN:
|
||||
raise MultipleArchitectures(args.command)
|
||||
raise MultipleArchitecturesError(args.command)
|
||||
|
||||
with Pool(len(architectures)) as pool:
|
||||
result = pool.starmap(
|
||||
@ -135,8 +135,8 @@ class Handler:
|
||||
return 0 if all(result) else 1
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -144,7 +144,7 @@ class Handler:
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
|
||||
Raises:
|
||||
|
@ -33,8 +33,8 @@ class Help(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -42,7 +42,7 @@ class Help(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
parser: argparse.ArgumentParser = args.parser()
|
||||
|
@ -34,8 +34,8 @@ class KeyImport(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -43,8 +43,8 @@ class KeyImport(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
Application(architecture, configuration, no_report, unsafe).repository.sign.key_import(
|
||||
args.key_server, args.key)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
application.repository.sign.key_import(args.key_server, args.key)
|
||||
|
@ -18,17 +18,19 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Type
|
||||
from typing import List, Optional, Tuple, Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import StringPrinter
|
||||
from ahriman.core.formatters import PatchPrinter
|
||||
from ahriman.models.action import Action
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
|
||||
|
||||
class Patch(Handler):
|
||||
@ -37,8 +39,8 @@ class Patch(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -46,56 +48,99 @@ class Patch(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
application.on_start()
|
||||
|
||||
if args.action == Action.List:
|
||||
Patch.patch_set_list(application, args.package, args.exit_code)
|
||||
if args.action == Action.Update and args.variable is not None:
|
||||
patch = Patch.patch_create_from_function(args.variable, args.patch)
|
||||
Patch.patch_set_create(application, args.package, patch)
|
||||
elif args.action == Action.Update and args.variable is None:
|
||||
package_base, patch = Patch.patch_create_from_diff(args.package, args.track)
|
||||
Patch.patch_set_create(application, package_base, patch)
|
||||
elif args.action == Action.List:
|
||||
Patch.patch_set_list(application, args.package, args.variable, args.exit_code)
|
||||
elif args.action == Action.Remove:
|
||||
Patch.patch_set_remove(application, args.package)
|
||||
elif args.action == Action.Update:
|
||||
Patch.patch_set_create(application, Path(args.package), args.track)
|
||||
Patch.patch_set_remove(application, args.package, args.variable)
|
||||
|
||||
@staticmethod
|
||||
def patch_set_create(application: Application, sources_dir: Path, track: List[str]) -> None:
|
||||
def patch_create_from_diff(sources_dir: Path, track: List[str]) -> Tuple[str, PkgbuildPatch]:
|
||||
"""
|
||||
create PKGBUILD plain diff patches from sources directory
|
||||
|
||||
Args:
|
||||
sources_dir(Path): path to directory with the package sources
|
||||
track(List[str]): track files which match the glob before creating the patch
|
||||
|
||||
Returns:
|
||||
Tuple[str, PkgbuildPatch]: package base and created PKGBUILD patch based on the diff from master HEAD
|
||||
to current files
|
||||
"""
|
||||
package = Package.from_build(sources_dir)
|
||||
patch = Sources.patch_create(sources_dir, *track)
|
||||
return package.base, PkgbuildPatch(None, patch)
|
||||
|
||||
@staticmethod
|
||||
def patch_create_from_function(variable: str, patch_path: Optional[Path]) -> PkgbuildPatch:
|
||||
"""
|
||||
create single-function patch set for the package base
|
||||
|
||||
Args:
|
||||
variable(str): function or variable name inside PKGBUILD
|
||||
patch_path(Path): optional path to patch content. If not set, it will be read from stdin
|
||||
|
||||
Returns:
|
||||
PkgbuildPatch: created patch for the PKGBUILD function
|
||||
"""
|
||||
if patch_path is None:
|
||||
print("Post new function or variable value below. Press Ctrl-D to finish:", file=sys.stderr)
|
||||
patch = "".join(list(sys.stdin))
|
||||
else:
|
||||
patch = patch_path.read_text(encoding="utf8")
|
||||
patch = patch.strip() # remove spaces around the patch
|
||||
return PkgbuildPatch(variable, patch)
|
||||
|
||||
@staticmethod
|
||||
def patch_set_create(application: Application, package_base: str, patch: PkgbuildPatch) -> None:
|
||||
"""
|
||||
create patch set for the package base
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
sources_dir(Path): path to directory with the package sources
|
||||
track(List[str]): track files which match the glob before creating the patch
|
||||
package_base(str): package base
|
||||
patch(PkgbuildPatch): patch descriptor
|
||||
"""
|
||||
package = Package.from_build(sources_dir)
|
||||
patch = Sources.patch_create(sources_dir, *track)
|
||||
application.database.patches_insert(package.base, patch)
|
||||
application.database.patches_insert(package_base, patch)
|
||||
|
||||
@staticmethod
|
||||
def patch_set_list(application: Application, package_base: Optional[str], exit_code: bool) -> None:
|
||||
def patch_set_list(application: Application, package_base: Optional[str], variables: List[str],
|
||||
exit_code: bool) -> None:
|
||||
"""
|
||||
list patches available for the package base
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
package_base(Optional[str]): package base
|
||||
variables(List[str]): extract patches only for specified PKGBUILD variables
|
||||
exit_code(bool): exit with error on empty search result
|
||||
:
|
||||
"""
|
||||
patches = application.database.patches_list(package_base)
|
||||
patches = application.database.patches_list(package_base, variables)
|
||||
Patch.check_if_empty(exit_code, not patches)
|
||||
|
||||
for base, patch in patches.items():
|
||||
content = base if package_base is None else patch
|
||||
StringPrinter(content).print(verbose=True)
|
||||
PatchPrinter(base, patch).print(verbose=True, separator=" = ")
|
||||
|
||||
@staticmethod
|
||||
def patch_set_remove(application: Application, package_base: str) -> None:
|
||||
def patch_set_remove(application: Application, package_base: str, variables: List[str]) -> None:
|
||||
"""
|
||||
remove patch set for the package base
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
package_base(str): package base
|
||||
variables(List[str]): remove patches only for specified PKGBUILD variables
|
||||
"""
|
||||
application.database.patches_remove(package_base)
|
||||
application.database.patches_remove(package_base, variables)
|
||||
|
@ -34,8 +34,8 @@ class Rebuild(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -43,16 +43,16 @@ class Rebuild(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
depends_on = set(args.depends_on) if args.depends_on else None
|
||||
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
if args.from_database:
|
||||
updates = Rebuild.extract_packages(application)
|
||||
else:
|
||||
updates = application.repository.packages_depends_on(depends_on)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
application.on_start()
|
||||
|
||||
packages = Rebuild.extract_packages(application, from_database=args.from_database)
|
||||
updates = application.repository.packages_depend_on(packages, depends_on)
|
||||
|
||||
Rebuild.check_if_empty(args.exit_code, not updates)
|
||||
if args.dry_run:
|
||||
@ -64,14 +64,17 @@ class Rebuild(Handler):
|
||||
Rebuild.check_if_empty(args.exit_code, result.is_empty)
|
||||
|
||||
@staticmethod
|
||||
def extract_packages(application: Application) -> List[Package]:
|
||||
def extract_packages(application: Application, *, from_database: bool) -> List[Package]:
|
||||
"""
|
||||
extract packages from database file
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
from_database(bool): extract packages from database instead of repository filesystem
|
||||
|
||||
Returns:
|
||||
List[Package]: list of packages which were stored in database
|
||||
"""
|
||||
if from_database:
|
||||
return application.repository.packages()
|
||||
return [package for (package, _) in application.database.packages_get()]
|
||||
|
@ -32,8 +32,8 @@ class Remove(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -41,7 +41,9 @@ class Remove(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
Application(architecture, configuration, no_report, unsafe).remove(args.package)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
application.on_start()
|
||||
application.remove(args.package)
|
||||
|
@ -33,8 +33,8 @@ class RemoveUnknown(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -42,15 +42,16 @@ class RemoveUnknown(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
application.on_start()
|
||||
unknown_packages = application.unknown()
|
||||
|
||||
if args.dry_run:
|
||||
for package in sorted(unknown_packages):
|
||||
StringPrinter(package).print(args.info)
|
||||
StringPrinter(package).print(False)
|
||||
return
|
||||
|
||||
application.remove(unknown_packages)
|
||||
|
@ -34,8 +34,8 @@ class Restore(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -43,7 +43,7 @@ class Restore(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
with TarFile(args.path) as archive:
|
||||
|
@ -26,7 +26,7 @@ from ahriman.application.application import Application
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.alpm.remote import AUR, Official
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import InvalidOption
|
||||
from ahriman.core.exceptions import OptionError
|
||||
from ahriman.core.formatters import AurPrinter
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
@ -43,8 +43,8 @@ class Search(Handler):
|
||||
SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list}
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -52,10 +52,10 @@ class Search(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
|
||||
official_packages_list = Official.multisearch(*args.search, pacman=application.repository.pacman)
|
||||
aur_packages_list = AUR.multisearch(*args.search, pacman=application.repository.pacman)
|
||||
@ -82,7 +82,7 @@ class Search(Handler):
|
||||
InvalidOption: if search fields is not in list of allowed ones
|
||||
"""
|
||||
if sort_by not in Search.SORT_FIELDS:
|
||||
raise InvalidOption(sort_by)
|
||||
raise OptionError(sort_by)
|
||||
# always sort by package name at the last
|
||||
# well technically it is not a string, but we can deal with it
|
||||
comparator: Callable[[AURPackage], Tuple[str, str]] =\
|
||||
|
@ -20,6 +20,7 @@
|
||||
import argparse
|
||||
|
||||
from pathlib import Path
|
||||
from pwd import getpwuid
|
||||
from typing import Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
@ -34,21 +35,19 @@ class Setup(Handler):
|
||||
|
||||
Attributes:
|
||||
ARCHBUILD_COMMAND_PATH(Path): (class attribute) default devtools command
|
||||
BIN_DIR_PATH(Path): (class attribute) directory for custom binaries
|
||||
MIRRORLIST_PATH(Path): (class attribute) path to pacman default mirrorlist (used by multilib repository)
|
||||
SUDOERS_PATH(Path): (class attribute) path to sudoers.d include configuration
|
||||
SUDOERS_DIR_PATH(Path): (class attribute) path to sudoers.d includes directory
|
||||
"""
|
||||
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||
|
||||
ARCHBUILD_COMMAND_PATH = Path("/usr/bin/archbuild")
|
||||
BIN_DIR_PATH = Path("/usr/local/bin")
|
||||
MIRRORLIST_PATH = Path("/etc/pacman.d/mirrorlist")
|
||||
SUDOERS_PATH = Path("/etc/sudoers.d/ahriman")
|
||||
SUDOERS_DIR_PATH = Path("/etc/sudoers.d")
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -56,39 +55,41 @@ class Setup(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration.include)
|
||||
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration.include,
|
||||
configuration.repository_paths)
|
||||
configuration.reload()
|
||||
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
|
||||
Setup.configuration_create_makepkg(args.packager, application.repository.paths)
|
||||
Setup.executable_create(args.build_command, architecture)
|
||||
Setup.executable_create(application.repository.paths, args.build_command, architecture)
|
||||
Setup.configuration_create_devtools(args.build_command, architecture, args.from_configuration,
|
||||
args.no_multilib, args.repository, application.repository.paths)
|
||||
Setup.configuration_create_sudo(args.build_command, architecture)
|
||||
args.multilib, args.repository, application.repository.paths)
|
||||
Setup.configuration_create_sudo(application.repository.paths, args.build_command, architecture)
|
||||
|
||||
application.repository.repo.init()
|
||||
|
||||
@staticmethod
|
||||
def build_command(prefix: str, architecture: str) -> Path:
|
||||
def build_command(root: Path, prefix: str, architecture: str) -> Path:
|
||||
"""
|
||||
generate build command name
|
||||
|
||||
Args:
|
||||
root(Path): root directory for the build command (must be root of the reporitory)
|
||||
prefix(str): command prefix in {prefix}-{architecture}-build
|
||||
architecture(str): repository architecture
|
||||
|
||||
Returns:
|
||||
Path: valid devtools command name
|
||||
"""
|
||||
return Setup.BIN_DIR_PATH / f"{prefix}-{architecture}-build"
|
||||
return root / f"{prefix}-{architecture}-build"
|
||||
|
||||
@staticmethod
|
||||
def configuration_create_ahriman(args: argparse.Namespace, architecture: str, repository: str,
|
||||
include_path: Path) -> None:
|
||||
include_path: Path, paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
create service specific configuration
|
||||
|
||||
@ -97,11 +98,13 @@ class Setup(Handler):
|
||||
architecture(str): repository architecture
|
||||
repository(str): repository name
|
||||
include_path(Path): path to directory with configuration includes
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
configuration = Configuration()
|
||||
|
||||
section = Configuration.section_name("build", architecture)
|
||||
configuration.set_option(section, "build_command", str(Setup.build_command(args.build_command, architecture)))
|
||||
build_command = Setup.build_command(paths.root, args.build_command, architecture)
|
||||
configuration.set_option(section, "build_command", str(build_command))
|
||||
configuration.set_option("repository", "name", repository)
|
||||
if args.build_as_user is not None:
|
||||
configuration.set_option(section, "makechrootpkg_flags", f"-U {args.build_as_user}")
|
||||
@ -121,19 +124,24 @@ class Setup(Handler):
|
||||
|
||||
@staticmethod
|
||||
def configuration_create_devtools(prefix: str, architecture: str, source: Path,
|
||||
no_multilib: bool, repository: str, paths: RepositoryPaths) -> None:
|
||||
multilib: bool, repository: str, paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
create configuration for devtools based on ``source`` configuration
|
||||
|
||||
Note:
|
||||
devtools does not allow to specify the pacman configuration, thus we still have to use configuration in /usr
|
||||
|
||||
Args:
|
||||
prefix(str): command prefix in {prefix}-{architecture}-build
|
||||
architecture(str): repository architecture
|
||||
source(Path): path to source configuration file
|
||||
no_multilib(bool): do not add multilib repository
|
||||
multilib(bool): add or do not multilib repository
|
||||
repository(str): repository name
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
configuration = Configuration()
|
||||
# allow_no_value=True is required because pacman uses boolean configuration in which just keys present
|
||||
# (e.g. NoProgressBar) which will lead to exception
|
||||
configuration = Configuration(allow_no_value=True)
|
||||
# preserve case
|
||||
# stupid mypy thinks that it is impossible
|
||||
configuration.optionxform = lambda key: key # type: ignore
|
||||
@ -146,7 +154,7 @@ class Setup(Handler):
|
||||
configuration.set_option("options", "Architecture", architecture)
|
||||
|
||||
# add multilib
|
||||
if not no_multilib:
|
||||
if multilib:
|
||||
configuration.set_option("multilib", "Include", str(Setup.MIRRORLIST_PATH))
|
||||
|
||||
# add repository itself
|
||||
@ -166,30 +174,36 @@ class Setup(Handler):
|
||||
packager(str): packager identifier (e.g. name, email)
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
(paths.root / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
|
||||
uid, _ = paths.root_owner
|
||||
home_dir = Path(getpwuid(uid).pw_dir)
|
||||
(home_dir / ".makepkg.conf").write_text(f"PACKAGER='{packager}'\n", encoding="utf8")
|
||||
|
||||
@staticmethod
|
||||
def configuration_create_sudo(prefix: str, architecture: str) -> None:
|
||||
def configuration_create_sudo(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
|
||||
"""
|
||||
create configuration to run build command with sudo without password
|
||||
|
||||
Args:
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
prefix(str): command prefix in {prefix}-{architecture}-build
|
||||
architecture(str): repository architecture
|
||||
"""
|
||||
command = Setup.build_command(prefix, architecture)
|
||||
Setup.SUDOERS_PATH.write_text(f"ahriman ALL=(ALL) NOPASSWD: {command} *\n", encoding="utf8")
|
||||
Setup.SUDOERS_PATH.chmod(0o400) # security!
|
||||
command = Setup.build_command(paths.root, prefix, architecture)
|
||||
sudoers_file = Setup.build_command(Setup.SUDOERS_DIR_PATH, prefix, architecture)
|
||||
sudoers_file.write_text(f"ahriman ALL=(ALL) NOPASSWD: {command} *\n", encoding="utf8")
|
||||
sudoers_file.chmod(0o400) # security!
|
||||
|
||||
@staticmethod
|
||||
def executable_create(prefix: str, architecture: str) -> None:
|
||||
def executable_create(paths: RepositoryPaths, prefix: str, architecture: str) -> None:
|
||||
"""
|
||||
create executable for the service
|
||||
|
||||
Args:
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
prefix(str): command prefix in {prefix}-{architecture}-build
|
||||
architecture(str): repository architecture
|
||||
"""
|
||||
command = Setup.build_command(prefix, architecture)
|
||||
command = Setup.build_command(paths.root, prefix, architecture)
|
||||
command.unlink(missing_ok=True)
|
||||
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
|
||||
paths.chown(command) # we would like to keep owner inside ahriman's home
|
||||
|
62
src/ahriman/application/handlers/shell.py
Normal file
62
src/ahriman/application/handlers/shell.py
Normal file
@ -0,0 +1,62 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
import code
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import StringPrinter
|
||||
|
||||
|
||||
class Shell(Handler):
|
||||
"""
|
||||
python shell handler
|
||||
"""
|
||||
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = False
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
# pylint: disable=possibly-unused-variable
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
if args.verbose:
|
||||
# licensed by https://creativecommons.org/licenses/by-sa/3.0
|
||||
path = Path(sys.prefix) / "share" / "ahriman" / "templates" / "shell"
|
||||
StringPrinter(path.read_text(encoding="utf8")).print(verbose=False)
|
||||
if args.code is None:
|
||||
code.interact(local=locals())
|
||||
else:
|
||||
code.InteractiveConsole(locals=locals()).runcode(args.code)
|
@ -32,8 +32,8 @@ class Sign(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -41,7 +41,7 @@ class Sign(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
Application(architecture, configuration, no_report, unsafe).sign(args.package)
|
||||
Application(architecture, configuration, report=report, unsafe=unsafe).sign(args.package)
|
||||
|
@ -37,8 +37,8 @@ class Status(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -46,17 +46,17 @@ class Status(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
# we are using reporter here
|
||||
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
|
||||
client = Application(architecture, configuration, report=True, unsafe=unsafe).repository.reporter
|
||||
if args.ahriman:
|
||||
ahriman = client.get_self()
|
||||
StatusPrinter(ahriman).print(args.info)
|
||||
service_status = client.get_internal()
|
||||
StatusPrinter(service_status.status).print(args.info)
|
||||
if args.package:
|
||||
packages: Iterable[Tuple[Package, BuildStatus]] = sum(
|
||||
[client.get(base) for base in args.package],
|
||||
(client.get(base) for base in args.package),
|
||||
start=[])
|
||||
else:
|
||||
packages = client.get(None)
|
||||
|
@ -35,8 +35,8 @@ class StatusUpdate(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -44,11 +44,11 @@ class StatusUpdate(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
# we are using reporter here
|
||||
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
|
||||
client = Application(architecture, configuration, report=True, unsafe=unsafe).repository.reporter
|
||||
|
||||
if args.action == Action.Update and args.package:
|
||||
# update packages statuses
|
||||
|
@ -33,8 +33,8 @@ class Triggers(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -42,7 +42,12 @@ class Triggers(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
Application(architecture, configuration, no_report, unsafe).repository.process_triggers(Result())
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe)
|
||||
if args.trigger:
|
||||
loader = application.repository.triggers
|
||||
loader.triggers = [loader.load_trigger(trigger) for trigger in args.trigger]
|
||||
application.on_start()
|
||||
application.on_result(Result())
|
||||
|
@ -35,8 +35,8 @@ class UnsafeCommands(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -44,7 +44,7 @@ class UnsafeCommands(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
parser = args.parser()
|
||||
@ -79,6 +79,7 @@ class UnsafeCommands(Handler):
|
||||
Returns:
|
||||
List[str]: list of commands with default unsafe flag
|
||||
"""
|
||||
# should never fail
|
||||
# pylint: disable=protected-access
|
||||
subparser = next(action for action in parser._actions if isinstance(action, argparse._SubParsersAction))
|
||||
return [action_name for action_name, action in subparser.choices.items() if action.get_default("unsafe")]
|
||||
|
@ -32,8 +32,8 @@ class Update(Handler):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -41,12 +41,14 @@ class Update(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
|
||||
Update.log_fn(application, args.dry_run))
|
||||
application = Application(architecture, configuration, report=report, unsafe=unsafe,
|
||||
refresh_pacman_database=args.refresh)
|
||||
application.on_start()
|
||||
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
|
||||
log_fn=Update.log_fn(application, args.dry_run))
|
||||
Update.check_if_empty(args.exit_code, not packages)
|
||||
if args.dry_run:
|
||||
return
|
||||
|
@ -39,8 +39,8 @@ class Users(Handler):
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -48,7 +48,7 @@ class Users(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
database = SQLite.load(configuration)
|
||||
@ -149,7 +149,7 @@ class Users(Handler):
|
||||
Returns:
|
||||
User: built user descriptor
|
||||
"""
|
||||
user = User(args.username, args.password, args.role)
|
||||
if user.password is None:
|
||||
user.password = getpass.getpass()
|
||||
return user
|
||||
password = args.password
|
||||
if password is None:
|
||||
password = getpass.getpass()
|
||||
return User(username=args.username, password=password, access=args.role)
|
||||
|
87
src/ahriman/application/handlers/versions.py
Normal file
87
src/ahriman/application/handlers/versions.py
Normal file
@ -0,0 +1,87 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
import pkg_resources
|
||||
import sys
|
||||
|
||||
from typing import Dict, List, Tuple, Type
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import VersionPrinter
|
||||
|
||||
|
||||
class Versions(Handler):
|
||||
"""
|
||||
version handler
|
||||
"""
|
||||
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
VersionPrinter(f"Module version {version.__version__}",
|
||||
{"Python": sys.version}).print(verbose=False, separator=" ")
|
||||
packages = Versions.package_dependencies("ahriman", ("pacman", "s3", "web"))
|
||||
VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ")
|
||||
|
||||
@staticmethod
|
||||
def package_dependencies(root: str, root_extras: Tuple[str, ...] = ()) -> Dict[str, str]:
|
||||
"""
|
||||
extract list of ahriman package dependencies installed into system with their versions
|
||||
|
||||
Args:
|
||||
root(str): root package name
|
||||
root_extras(Tuple[str, ...]): extras for the root package (Default value = ())
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: map of installed dependency to its version
|
||||
"""
|
||||
resources: Dict[str, pkg_resources.Distribution] = pkg_resources.working_set.by_key # type: ignore
|
||||
|
||||
def dependencies_by_key(key: str, extras: Tuple[str, ...] = ()) -> List[str]:
|
||||
return [entry.key for entry in resources[key].requires(extras)]
|
||||
|
||||
keys: List[str] = []
|
||||
portion = {key for key in dependencies_by_key(root, root_extras) if key in resources}
|
||||
while portion:
|
||||
keys.extend(portion)
|
||||
portion = {
|
||||
key
|
||||
for key in sum((dependencies_by_key(key) for key in portion), start=[])
|
||||
if key not in keys and key in resources
|
||||
}
|
||||
|
||||
return {
|
||||
resource.project_name: resource.version
|
||||
for resource in map(lambda key: resources[key], keys)
|
||||
}
|
@ -35,8 +35,8 @@ class Web(Handler):
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||
report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
@ -44,7 +44,7 @@ class Web(Handler):
|
||||
args(argparse.Namespace): command line args
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
no_report(bool): force disable reporting
|
||||
report(bool): force enable or disable reporting
|
||||
unsafe(bool): if set no user check will be performed before path creation
|
||||
"""
|
||||
# we are using local import for optional dependencies
|
||||
|
@ -20,7 +20,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
from types import TracebackType
|
||||
@ -28,13 +27,14 @@ from typing import Literal, Optional, Type
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import DuplicateRun
|
||||
from ahriman.core.exceptions import DuplicateRunError
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.core.status.client import Client
|
||||
from ahriman.core.util import check_user
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
|
||||
|
||||
class Lock:
|
||||
class Lock(LazyLogging):
|
||||
"""
|
||||
wrapper for application lock file
|
||||
|
||||
@ -73,7 +73,7 @@ class Lock:
|
||||
self.unsafe = args.unsafe
|
||||
|
||||
self.paths = configuration.repository_paths
|
||||
self.reporter = Client() if args.no_report else Client.load(configuration)
|
||||
self.reporter = Client.load(configuration) if args.report else Client()
|
||||
|
||||
def __enter__(self) -> Lock:
|
||||
"""
|
||||
@ -115,16 +115,14 @@ class Lock:
|
||||
"""
|
||||
status = self.reporter.get_internal()
|
||||
if status.version is not None and status.version != version.__version__:
|
||||
logging.getLogger("root").warning(
|
||||
"status watcher version mismatch, our %s, their %s",
|
||||
version.__version__,
|
||||
status.version)
|
||||
self.logger.warning("status watcher version mismatch, our %s, their %s",
|
||||
version.__version__, status.version)
|
||||
|
||||
def check_user(self) -> None:
|
||||
"""
|
||||
check if current user is actually owner of ahriman root
|
||||
"""
|
||||
check_user(self.paths, self.unsafe)
|
||||
check_user(self.paths, unsafe=self.unsafe)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
@ -146,4 +144,4 @@ class Lock:
|
||||
try:
|
||||
self.path.touch(exist_ok=self.force)
|
||||
except FileExistsError:
|
||||
raise DuplicateRun()
|
||||
raise DuplicateRunError()
|
||||
|
@ -17,13 +17,18 @@
|
||||
# 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 pyalpm import Handle, Package, SIG_PACKAGE # type: ignore
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
|
||||
from typing import Generator, Set
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
class Pacman:
|
||||
class Pacman(LazyLogging):
|
||||
"""
|
||||
alpm wrapper
|
||||
|
||||
@ -31,35 +36,96 @@ class Pacman:
|
||||
handle(Handle): pyalpm root ``Handle``
|
||||
"""
|
||||
|
||||
def __init__(self, configuration: Configuration) -> None:
|
||||
def __init__(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> None:
|
||||
"""
|
||||
default constructor
|
||||
|
||||
Args:
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
refresh_database(int): synchronize local cache to remote. If set to ``0``, no syncronization will be
|
||||
enabled, if set to ``1`` - normal syncronization, if set to ``2`` - force syncronization
|
||||
"""
|
||||
root = configuration.get("alpm", "root")
|
||||
root = configuration.getpath("alpm", "root")
|
||||
pacman_root = configuration.getpath("alpm", "database")
|
||||
self.handle = Handle(root, str(pacman_root))
|
||||
for repository in configuration.getlist("alpm", "repositories"):
|
||||
self.handle.register_syncdb(repository, SIG_PACKAGE)
|
||||
use_ahriman_cache = configuration.getboolean("alpm", "use_ahriman_cache")
|
||||
mirror = configuration.get("alpm", "mirror")
|
||||
paths = configuration.repository_paths
|
||||
database_path = paths.pacman if use_ahriman_cache else pacman_root
|
||||
|
||||
def all_packages(self) -> Set[str]:
|
||||
self.handle = Handle(str(root), str(database_path))
|
||||
for repository in configuration.getlist("alpm", "repositories"):
|
||||
database = self.database_init(repository, mirror, architecture)
|
||||
self.database_copy(database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
|
||||
|
||||
if use_ahriman_cache and refresh_database:
|
||||
self.database_sync(refresh_database > 1)
|
||||
|
||||
def database_copy(self, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
|
||||
use_ahriman_cache: bool) -> None:
|
||||
"""
|
||||
get list of packages known for alpm
|
||||
copy database from the operating system root to the ahriman local home
|
||||
|
||||
Args:
|
||||
database(DB): pacman database instance to be copied
|
||||
pacman_root(Path): operating system pacman's root
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
use_ahriman_cache(bool): use local ahriman cache instead of system one
|
||||
"""
|
||||
def repository_database(root: Path) -> Path:
|
||||
return root / "sync" / f"{database.name}.db"
|
||||
|
||||
if not use_ahriman_cache:
|
||||
return
|
||||
# copy root database if no local copy found
|
||||
pacman_db_path = Path(self.handle.dbpath)
|
||||
if not pacman_db_path.is_dir():
|
||||
return # root directory does not exist yet
|
||||
dst = repository_database(pacman_db_path)
|
||||
if dst.is_file():
|
||||
return # file already exists, do not copy
|
||||
src = repository_database(pacman_root)
|
||||
if not src.is_file():
|
||||
self.logger.warning("repository %s is set to be used, however, no working copy was found", database.name)
|
||||
return # database for some reasons deos not exist
|
||||
self.logger.info("copy pacman database from operating system root to ahriman's home")
|
||||
shutil.copy(src, dst)
|
||||
paths.chown(dst)
|
||||
|
||||
def database_init(self, repository: str, mirror: str, architecture: str) -> DB:
|
||||
"""
|
||||
create database instance from pacman handler and set its properties
|
||||
|
||||
Args:
|
||||
repository(str): pacman repository name (e.g. core)
|
||||
mirror(str): arch linux mirror url
|
||||
architecture(str): repository architecture
|
||||
|
||||
Returns:
|
||||
Set[str]: list of package names
|
||||
DB: loaded pacman database instance
|
||||
"""
|
||||
result: Set[str] = set()
|
||||
database: DB = self.handle.register_syncdb(repository, SIG_PACKAGE)
|
||||
# replace variables in mirror address
|
||||
database.servers = [mirror.replace("$repo", repository).replace("$arch", architecture)]
|
||||
return database
|
||||
|
||||
def database_sync(self, force: bool) -> None:
|
||||
"""
|
||||
sync local database
|
||||
|
||||
Args:
|
||||
force(bool): force database syncronization (same as ``pacman -Syy``)
|
||||
"""
|
||||
self.logger.info("refresh ahriman's home pacman database (force refresh %s)", force)
|
||||
transaction = self.handle.init_transaction()
|
||||
for database in self.handle.get_syncdbs():
|
||||
for package in database.pkgcache:
|
||||
result.add(package.name) # package itself
|
||||
result.update(package.provides) # provides list for meta-packages
|
||||
try:
|
||||
database.update(force)
|
||||
except PyalpmError:
|
||||
self.logger.exception("exception during update %s", database.name)
|
||||
transaction.release()
|
||||
|
||||
return result
|
||||
|
||||
def get(self, package_name: str) -> Generator[Package, None, None]:
|
||||
def package_get(self, package_name: str) -> Generator[Package, None, None]:
|
||||
"""
|
||||
retrieve list of the packages from the repository by name
|
||||
|
||||
@ -74,3 +140,18 @@ class Pacman:
|
||||
if package is None:
|
||||
continue
|
||||
yield package
|
||||
|
||||
def packages(self) -> Set[str]:
|
||||
"""
|
||||
get list of packages known for alpm
|
||||
|
||||
Returns:
|
||||
Set[str]: list of package names
|
||||
"""
|
||||
result: Set[str] = set()
|
||||
for database in self.handle.get_syncdbs():
|
||||
for package in database.pkgcache:
|
||||
result.add(package.name) # package itself
|
||||
result.update(package.provides) # provides list for meta-packages
|
||||
|
||||
return result
|
||||
|
@ -23,7 +23,7 @@ from typing import Any, Dict, List, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote import Remote
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
|
||||
from ahriman.core.util import exception_response_text
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
@ -36,11 +36,13 @@ class AUR(Remote):
|
||||
DEFAULT_AUR_URL(str): (class attribute) default AUR url
|
||||
DEFAULT_RPC_URL(str): (class attribute) default AUR RPC url
|
||||
DEFAULT_RPC_VERSION(str): (class attribute) default AUR RPC version
|
||||
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
|
||||
"""
|
||||
|
||||
DEFAULT_AUR_URL = "https://aur.archlinux.org"
|
||||
DEFAULT_RPC_URL = f"{DEFAULT_AUR_URL}/rpc"
|
||||
DEFAULT_RPC_VERSION = "5"
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
@staticmethod
|
||||
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
|
||||
@ -59,7 +61,7 @@ class AUR(Remote):
|
||||
response_type = response["type"]
|
||||
if response_type == "error":
|
||||
error_details = response.get("error", "Unknown API error")
|
||||
raise InvalidPackageInfo(error_details)
|
||||
raise PackageInfoError(error_details)
|
||||
return [AURPackage.from_json(package) for package in response["results"]]
|
||||
|
||||
@classmethod
|
||||
@ -113,7 +115,7 @@ class AUR(Remote):
|
||||
query[key] = value
|
||||
|
||||
try:
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params=query)
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params=query, timeout=self.DEFAULT_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
return self.parse_response(response.json())
|
||||
except requests.HTTPError as e:
|
||||
@ -138,7 +140,10 @@ class AUR(Remote):
|
||||
AURPackage: package which match the package name
|
||||
"""
|
||||
packages = self.make_request("info", package_name)
|
||||
return next(package for package in packages if package.name == package_name)
|
||||
try:
|
||||
return next(package for package in packages if package.name == package_name)
|
||||
except StopIteration:
|
||||
raise UnknownPackageError(package_name)
|
||||
|
||||
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
|
@ -23,7 +23,7 @@ from typing import Any, Dict, List, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote import Remote
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
|
||||
from ahriman.core.util import exception_response_text
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
@ -36,11 +36,13 @@ class Official(Remote):
|
||||
DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url
|
||||
DEFAULT_SEARCH_REPOSITORIES(List[str]): (class attribute) default list of repositories to search
|
||||
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
|
||||
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
|
||||
"""
|
||||
|
||||
DEFAULT_ARCHLINUX_URL = "https://archlinux.org"
|
||||
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib", "Community"]
|
||||
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
@staticmethod
|
||||
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
|
||||
@ -57,7 +59,7 @@ class Official(Remote):
|
||||
InvalidPackageInfo: for error API response
|
||||
"""
|
||||
if not response["valid"]:
|
||||
raise InvalidPackageInfo("API validation error")
|
||||
raise PackageInfoError("API validation error")
|
||||
return [AURPackage.from_repo(package) for package in response["results"]]
|
||||
|
||||
@classmethod
|
||||
@ -101,7 +103,10 @@ class Official(Remote):
|
||||
List[AURPackage]: response parsed to package list
|
||||
"""
|
||||
try:
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES})
|
||||
response = requests.get(
|
||||
self.DEFAULT_RPC_URL,
|
||||
params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES},
|
||||
timeout=self.DEFAULT_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
return self.parse_response(response.json())
|
||||
except requests.HTTPError as e:
|
||||
@ -123,7 +128,10 @@ class Official(Remote):
|
||||
AURPackage: package which match the package name
|
||||
"""
|
||||
packages = self.make_request(package_name, by="name")
|
||||
return next(package for package in packages if package.name == package_name)
|
||||
try:
|
||||
return next(package for package in packages if package.name == package_name)
|
||||
except StopIteration:
|
||||
raise UnknownPackageError(package_name)
|
||||
|
||||
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]:
|
||||
"""
|
||||
|
@ -19,6 +19,7 @@
|
||||
#
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote import Official
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
|
||||
@ -48,4 +49,7 @@ class OfficialSyncdb(Official):
|
||||
Returns:
|
||||
AURPackage: package which match the package name
|
||||
"""
|
||||
return next(AURPackage.from_pacman(package) for package in pacman.get(package_name))
|
||||
try:
|
||||
return next(AURPackage.from_pacman(package) for package in pacman.package_get(package_name))
|
||||
except StopIteration:
|
||||
raise UnknownPackageError(package_name)
|
||||
|
@ -19,21 +19,17 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Dict, List, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
|
||||
|
||||
class Remote:
|
||||
class Remote(LazyLogging):
|
||||
"""
|
||||
base class for remote package search
|
||||
|
||||
Attributes:
|
||||
logger(logging.Logger): class logger
|
||||
|
||||
Examples:
|
||||
These classes are designed to be used without instancing. In order to achieve it several class methods are
|
||||
provided: ``info``, ``multisearch`` and ``search``. Thus, the basic flow is the following::
|
||||
@ -47,12 +43,6 @@ class Remote:
|
||||
directly, whereas ``multisearch`` splits search one by one and finds intersection between search results.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
default constructor
|
||||
"""
|
||||
self.logger = logging.getLogger("build_details")
|
||||
|
||||
@classmethod
|
||||
def info(cls: Type[Remote], package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||
"""
|
||||
|
@ -17,22 +17,20 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from ahriman.core.exceptions import BuildFailed
|
||||
from ahriman.core.exceptions import BuildError
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.core.util import check_output
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
class Repo:
|
||||
class Repo(LazyLogging):
|
||||
"""
|
||||
repo-add and repo-remove wrapper
|
||||
|
||||
Attributes:
|
||||
logger(logging.Logger): class logger
|
||||
name(str): repository name
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
sign_args(List[str]): additional args which have to be used to sign repository archive
|
||||
@ -50,7 +48,6 @@ class Repo:
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
sign_args(List[str]): additional args which have to be used to sign repository archive
|
||||
"""
|
||||
self.logger = logging.getLogger("build_details")
|
||||
self.name = name
|
||||
self.paths = paths
|
||||
self.uid, _ = paths.root_owner
|
||||
@ -75,7 +72,7 @@ class Repo:
|
||||
"""
|
||||
Repo._check_output(
|
||||
"repo-add", *self.sign_args, "-R", str(self.repo_path), str(path),
|
||||
exception=BuildFailed(path.name),
|
||||
exception=BuildError(path.name),
|
||||
cwd=self.paths.repository,
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
@ -84,12 +81,8 @@ class Repo:
|
||||
"""
|
||||
create empty repository database
|
||||
"""
|
||||
Repo._check_output(
|
||||
"repo-add", *self.sign_args, str(self.repo_path),
|
||||
exception=None,
|
||||
cwd=self.paths.repository,
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
Repo._check_output("repo-add", *self.sign_args, str(self.repo_path),
|
||||
cwd=self.paths.repository, logger=self.logger, user=self.uid)
|
||||
|
||||
def remove(self, package: str, filename: Path) -> None:
|
||||
"""
|
||||
@ -106,7 +99,7 @@ class Repo:
|
||||
# remove package from registry
|
||||
Repo._check_output(
|
||||
"repo-remove", *self.sign_args, str(self.repo_path), package,
|
||||
exception=BuildFailed(package),
|
||||
exception=BuildError(package),
|
||||
cwd=self.paths.repository,
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
|
@ -19,25 +19,23 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Optional, Type
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database import SQLite
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.models.auth_settings import AuthSettings
|
||||
from ahriman.models.user_access import UserAccess
|
||||
|
||||
|
||||
class Auth:
|
||||
class Auth(LazyLogging):
|
||||
"""
|
||||
helper to deal with user authorization
|
||||
|
||||
Attributes:
|
||||
enabled(bool): indicates if authorization is enabled
|
||||
logger(logging.Logger): class logger
|
||||
max_age(int): session age in seconds. It will be used for both client side and server side checks
|
||||
safe_build_status(bool): allow read only access to the index page
|
||||
allow_read_only(bool): allow read only access to APIs
|
||||
"""
|
||||
|
||||
def __init__(self, configuration: Configuration, provider: AuthSettings = AuthSettings.Disabled) -> None:
|
||||
@ -48,9 +46,7 @@ class Auth:
|
||||
configuration(Configuration): configuration instance
|
||||
provider(AuthSettings, optional): authorization type definition (Default value = AuthSettings.Disabled)
|
||||
"""
|
||||
self.logger = logging.getLogger("http")
|
||||
|
||||
self.safe_build_status = configuration.getboolean("auth", "safe_build_status")
|
||||
self.allow_read_only = configuration.getboolean("auth", "allow_read_only")
|
||||
|
||||
self.enabled = provider.is_enabled
|
||||
self.max_age = configuration.getint("auth", "max_age", fallback=7 * 24 * 3600)
|
||||
@ -89,7 +85,7 @@ class Auth:
|
||||
return OAuth(configuration, database)
|
||||
return cls(configuration)
|
||||
|
||||
async def check_credentials(self, username: Optional[str], password: Optional[str]) -> bool: # pylint: disable=no-self-use
|
||||
async def check_credentials(self, username: Optional[str], password: Optional[str]) -> bool:
|
||||
"""
|
||||
validate user password
|
||||
|
||||
@ -103,7 +99,7 @@ class Auth:
|
||||
del username, password
|
||||
return True
|
||||
|
||||
async def known_username(self, username: Optional[str]) -> bool: # pylint: disable=no-self-use
|
||||
async def known_username(self, username: Optional[str]) -> bool:
|
||||
"""
|
||||
check if user is known
|
||||
|
||||
@ -116,7 +112,7 @@ class Auth:
|
||||
del username
|
||||
return True
|
||||
|
||||
async def verify_access(self, username: str, required: UserAccess, context: Optional[str]) -> bool: # pylint: disable=no-self-use
|
||||
async def verify_access(self, username: str, required: UserAccess, context: Optional[str]) -> bool:
|
||||
"""
|
||||
validate if user has access to requested resource
|
||||
|
||||
|
@ -24,7 +24,7 @@ from typing import Optional, Type
|
||||
from ahriman.core.auth import Mapping
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database import SQLite
|
||||
from ahriman.core.exceptions import InvalidOption
|
||||
from ahriman.core.exceptions import OptionError
|
||||
from ahriman.models.auth_settings import AuthSettings
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ class OAuth(Mapping):
|
||||
self.client_secret = configuration.get("auth", "client_secret")
|
||||
# in order to use OAuth feature the service must be publicity available
|
||||
# thus we expect that address is set
|
||||
self.redirect_uri = f"""{configuration.get("web", "address")}/user-api/v1/login"""
|
||||
self.redirect_uri = f"""{configuration.get("web", "address")}/api/v1/login"""
|
||||
self.provider = self.get_provider(configuration.get("auth", "oauth_provider"))
|
||||
# it is list but we will have to convert to string it anyway
|
||||
self.scopes = configuration.get("auth", "oauth_scopes")
|
||||
@ -69,7 +69,7 @@ class OAuth(Mapping):
|
||||
Returns:
|
||||
str: login control as html code to insert
|
||||
"""
|
||||
return """<a class="nav-link" href="/user-api/v1/login" title="login via OAuth2">login</a>"""
|
||||
return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2">login</a>"""
|
||||
|
||||
@staticmethod
|
||||
def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]:
|
||||
@ -91,7 +91,7 @@ class OAuth(Mapping):
|
||||
except TypeError: # what if it is random string?
|
||||
is_oauth2_client = False
|
||||
if not is_oauth2_client:
|
||||
raise InvalidOption(name)
|
||||
raise OptionError(name)
|
||||
return provider
|
||||
|
||||
def get_client(self) -> aioauth_client.OAuth2Client:
|
||||
|
@ -17,81 +17,50 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
import datetime
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.core.util import check_output, walk
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
class Sources:
|
||||
class Sources(LazyLogging):
|
||||
"""
|
||||
helper to download package sources (PKGBUILD etc)
|
||||
|
||||
Attributes:
|
||||
DEFAULT_BRANCH(str): (class attribute) default branch to process git repositories.
|
||||
Must be used only for local stored repositories, use RemoteSource descriptor instead for real packages
|
||||
logger(logging.Logger): (class attribute) class logger
|
||||
"""
|
||||
|
||||
DEFAULT_BRANCH = "master" # default fallback branch
|
||||
logger = logging.getLogger("build_details")
|
||||
|
||||
_check_output = check_output
|
||||
|
||||
@staticmethod
|
||||
def _add(sources_dir: Path, *pattern: str) -> None:
|
||||
def extend_architectures(sources_dir: Path, architecture: str) -> List[PkgbuildPatch]:
|
||||
"""
|
||||
track found files via git
|
||||
extend existing PKGBUILD with repository architecture
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to git repository
|
||||
*pattern(str): glob patterns
|
||||
"""
|
||||
# glob directory to find files which match the specified patterns
|
||||
found_files: List[Path] = []
|
||||
for glob in pattern:
|
||||
found_files.extend(sources_dir.glob(glob))
|
||||
if not found_files:
|
||||
return # no additional files found
|
||||
Sources.logger.info("found matching files %s", found_files)
|
||||
# add them to index
|
||||
Sources._check_output("git", "add", "--intent-to-add",
|
||||
*[str(fn.relative_to(sources_dir)) for fn in found_files],
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
|
||||
@staticmethod
|
||||
def _diff(sources_dir: Path) -> str:
|
||||
"""
|
||||
generate diff from the current version and write it to the output file
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to git repository
|
||||
sources_dir(Path): local path to directory with source files
|
||||
architecture(str): repository architecture
|
||||
|
||||
Returns:
|
||||
str: patch as plain string
|
||||
List[PkgbuildPatch]: generated patch for PKGBUILD architectures if required
|
||||
"""
|
||||
return Sources._check_output("git", "diff", exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
|
||||
@staticmethod
|
||||
def _move(pkgbuild_dir: Path, sources_dir: Path) -> None:
|
||||
"""
|
||||
move content from pkgbuild_dir to sources_dir
|
||||
|
||||
Args:
|
||||
pkgbuild_dir(Path): path to directory with pkgbuild from which need to move
|
||||
sources_dir(Path): path to target directory
|
||||
"""
|
||||
if pkgbuild_dir == sources_dir:
|
||||
return # directories are the same, no need to move
|
||||
for src in walk(pkgbuild_dir):
|
||||
dst = sources_dir / src.relative_to(pkgbuild_dir)
|
||||
shutil.move(src, dst)
|
||||
architectures = Package.supported_architectures(sources_dir)
|
||||
if "any" in architectures: # makepkg does not like when there is any other arch except for any
|
||||
return []
|
||||
architectures.add(architecture)
|
||||
return [PkgbuildPatch("arch", list(architectures))]
|
||||
|
||||
@staticmethod
|
||||
def fetch(sources_dir: Path, remote: Optional[RemoteSource]) -> None:
|
||||
@ -102,37 +71,34 @@ class Sources:
|
||||
sources_dir(Path): local path to fetch
|
||||
remote(Optional[RemoteSource]): remote target (from where to fetch)
|
||||
"""
|
||||
instance = Sources()
|
||||
# local directory exists and there is .git directory
|
||||
is_initialized_git = (sources_dir / ".git").is_dir()
|
||||
if is_initialized_git and not Sources.has_remotes(sources_dir):
|
||||
if is_initialized_git and not instance.has_remotes(sources_dir):
|
||||
# there is git repository, but no remote configured so far
|
||||
Sources.logger.info("skip update at %s because there are no branches configured", sources_dir)
|
||||
instance.logger.info("skip update at %s because there are no branches configured", sources_dir)
|
||||
return
|
||||
|
||||
branch = remote.branch if remote is not None else Sources.DEFAULT_BRANCH
|
||||
branch = remote.branch if remote is not None else instance.DEFAULT_BRANCH
|
||||
if is_initialized_git:
|
||||
Sources.logger.info("update HEAD to remote at %s using branch %s", sources_dir, branch)
|
||||
Sources._check_output("git", "fetch", "origin", branch,
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
instance.logger.info("update HEAD to remote at %s using branch %s", sources_dir, branch)
|
||||
Sources._check_output("git", "fetch", "origin", branch, cwd=sources_dir, logger=instance.logger)
|
||||
elif remote is not None:
|
||||
Sources.logger.info("clone remote %s to %s using branch %s", remote.git_url, sources_dir, branch)
|
||||
instance.logger.info("clone remote %s to %s using branch %s", remote.git_url, sources_dir, branch)
|
||||
Sources._check_output("git", "clone", "--branch", branch, "--single-branch",
|
||||
remote.git_url, str(sources_dir),
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
remote.git_url, str(sources_dir), cwd=sources_dir.parent, logger=instance.logger)
|
||||
else:
|
||||
# it will cause an exception later
|
||||
Sources.logger.error("%s is not initialized, but no remote provided", sources_dir)
|
||||
instance.logger.error("%s is not initialized, but no remote provided", sources_dir)
|
||||
|
||||
# and now force reset to our branch
|
||||
Sources._check_output("git", "checkout", "--force", branch,
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
Sources._check_output("git", "reset", "--hard", f"origin/{branch}",
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
Sources._check_output("git", "checkout", "--force", branch, cwd=sources_dir, logger=instance.logger)
|
||||
Sources._check_output("git", "reset", "--hard", f"origin/{branch}", cwd=sources_dir, logger=instance.logger)
|
||||
|
||||
# move content if required
|
||||
# we are using full path to source directory in order to make append possible
|
||||
pkgbuild_dir = remote.pkgbuild_dir if remote is not None else sources_dir.resolve()
|
||||
Sources._move((sources_dir / pkgbuild_dir).resolve(), sources_dir)
|
||||
instance.move((sources_dir / pkgbuild_dir).resolve(), sources_dir)
|
||||
|
||||
@staticmethod
|
||||
def has_remotes(sources_dir: Path) -> bool:
|
||||
@ -145,7 +111,8 @@ class Sources:
|
||||
Returns:
|
||||
bool: True in case if there is any remote and false otherwise
|
||||
"""
|
||||
remotes = Sources._check_output("git", "remote", exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
instance = Sources()
|
||||
remotes = Sources._check_output("git", "remote", cwd=sources_dir, logger=instance.logger)
|
||||
return bool(remotes)
|
||||
|
||||
@staticmethod
|
||||
@ -156,43 +123,30 @@ class Sources:
|
||||
Args:
|
||||
sources_dir(Path): local path to sources
|
||||
"""
|
||||
Sources._check_output("git", "init", "--initial-branch", Sources.DEFAULT_BRANCH,
|
||||
exception=None, cwd=sources_dir, logger=Sources.logger)
|
||||
instance = Sources()
|
||||
Sources._check_output("git", "init", "--initial-branch", instance.DEFAULT_BRANCH,
|
||||
cwd=sources_dir, logger=instance.logger)
|
||||
|
||||
@staticmethod
|
||||
def load(sources_dir: Path, package: Package, patch: Optional[str], paths: RepositoryPaths) -> None:
|
||||
def load(sources_dir: Path, package: Package, patches: List[PkgbuildPatch], paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
fetch sources from remote and apply patches
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to fetch
|
||||
package(Package): package definitions
|
||||
patch(Optional[str]): optional patch to be applied
|
||||
patches(List[PkgbuildPatch]): optional patch to be applied
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
instance = Sources()
|
||||
if (cache_dir := paths.cache_for(package.base)).is_dir() and cache_dir != sources_dir:
|
||||
# no need to clone whole repository, just copy from cache first
|
||||
shutil.copytree(cache_dir, sources_dir, dirs_exist_ok=True)
|
||||
Sources.fetch(sources_dir, package.remote)
|
||||
instance.fetch(sources_dir, package.remote)
|
||||
|
||||
if patch is None:
|
||||
Sources.logger.info("no patches found")
|
||||
return
|
||||
Sources.patch_apply(sources_dir, patch)
|
||||
|
||||
@staticmethod
|
||||
def patch_apply(sources_dir: Path, patch: str) -> None:
|
||||
"""
|
||||
apply patches if any
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to directory with git sources
|
||||
patch(str): patch to be applied
|
||||
"""
|
||||
# create patch
|
||||
Sources.logger.info("apply patch from database")
|
||||
Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace",
|
||||
exception=None, cwd=sources_dir, input_data=patch, logger=Sources.logger)
|
||||
patches.extend(instance.extend_architectures(sources_dir, paths.architecture))
|
||||
for patch in patches:
|
||||
instance.patch_apply(sources_dir, patch)
|
||||
|
||||
@staticmethod
|
||||
def patch_create(sources_dir: Path, *pattern: str) -> str:
|
||||
@ -206,6 +160,99 @@ class Sources:
|
||||
Returns:
|
||||
str: patch as plain text
|
||||
"""
|
||||
Sources._add(sources_dir, *pattern)
|
||||
diff = Sources._diff(sources_dir)
|
||||
instance = Sources()
|
||||
instance.add(sources_dir, *pattern)
|
||||
diff = instance.diff(sources_dir)
|
||||
return f"{diff}\n" # otherwise, patch will be broken
|
||||
|
||||
@staticmethod
|
||||
def push(sources_dir: Path, remote: RemoteSource, *pattern: str) -> None:
|
||||
"""
|
||||
commit selected changes and push files to the remote repository
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to git repository
|
||||
remote(RemoteSource): remote target, branch and url
|
||||
*pattern(str): glob patterns
|
||||
"""
|
||||
instance = Sources()
|
||||
instance.add(sources_dir, *pattern)
|
||||
instance.commit(sources_dir)
|
||||
Sources._check_output("git", "push", remote.git_url, remote.branch, cwd=sources_dir, logger=instance.logger)
|
||||
|
||||
def add(self, sources_dir: Path, *pattern: str) -> None:
|
||||
"""
|
||||
track found files via git
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to git repository
|
||||
*pattern(str): glob patterns
|
||||
"""
|
||||
# glob directory to find files which match the specified patterns
|
||||
found_files: List[Path] = []
|
||||
for glob in pattern:
|
||||
found_files.extend(sources_dir.glob(glob))
|
||||
if not found_files:
|
||||
return # no additional files found
|
||||
self.logger.info("found matching files %s", found_files)
|
||||
# add them to index
|
||||
Sources._check_output("git", "add", "--intent-to-add",
|
||||
*[str(fn.relative_to(sources_dir)) for fn in found_files],
|
||||
cwd=sources_dir, logger=self.logger)
|
||||
|
||||
def commit(self, sources_dir: Path, commit_message: Optional[str] = None) -> None:
|
||||
"""
|
||||
commit changes
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to git repository
|
||||
commit_message(Optional[str]): optional commit message if any. If none set, message will be generated
|
||||
according to the current timestamp
|
||||
"""
|
||||
if commit_message is None:
|
||||
commit_message = f"Autogenerated commit at {datetime.datetime.utcnow()}"
|
||||
Sources._check_output("git", "commit", "--allow-empty", "--message", commit_message,
|
||||
cwd=sources_dir, logger=self.logger)
|
||||
|
||||
def diff(self, sources_dir: Path) -> str:
|
||||
"""
|
||||
generate diff from the current version and write it to the output file
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to git repository
|
||||
|
||||
Returns:
|
||||
str: patch as plain string
|
||||
"""
|
||||
return Sources._check_output("git", "diff", cwd=sources_dir, logger=self.logger)
|
||||
|
||||
def move(self, pkgbuild_dir: Path, sources_dir: Path) -> None:
|
||||
"""
|
||||
move content from pkgbuild_dir to sources_dir
|
||||
|
||||
Args:
|
||||
pkgbuild_dir(Path): path to directory with pkgbuild from which need to move
|
||||
sources_dir(Path): path to target directory
|
||||
"""
|
||||
del self
|
||||
if pkgbuild_dir == sources_dir:
|
||||
return # directories are the same, no need to move
|
||||
for src in walk(pkgbuild_dir):
|
||||
dst = sources_dir / src.relative_to(pkgbuild_dir)
|
||||
shutil.move(src, dst)
|
||||
|
||||
def patch_apply(self, sources_dir: Path, patch: PkgbuildPatch) -> None:
|
||||
"""
|
||||
apply patches if any
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to directory with git sources
|
||||
patch(PkgbuildPatch): patch to be applied
|
||||
"""
|
||||
# create patch
|
||||
self.logger.info("apply patch %s from database at %s", patch.key, sources_dir)
|
||||
if patch.is_plain_diff:
|
||||
Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace",
|
||||
cwd=sources_dir, input_data=patch.serialize(), logger=self.logger)
|
||||
else:
|
||||
patch.write(sources_dir / "PKGBUILD")
|
||||
|
@ -17,27 +17,24 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database import SQLite
|
||||
from ahriman.core.exceptions import BuildFailed
|
||||
from ahriman.core.exceptions import BuildError
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.core.util import check_output
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
class Task:
|
||||
class Task(LazyLogging):
|
||||
"""
|
||||
base package build task
|
||||
|
||||
Attributes:
|
||||
build_logger(logging.Logger): logger for build process
|
||||
logger(logging.Logger): class logger
|
||||
package(Package): package definitions
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
uid(int): uid of the repository owner user
|
||||
@ -54,8 +51,6 @@ class Task:
|
||||
configuration(Configuration): configuration instance
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
self.logger = logging.getLogger("root")
|
||||
self.build_logger = logging.getLogger("build_details")
|
||||
self.package = package
|
||||
self.paths = paths
|
||||
self.uid, _ = paths.root_owner
|
||||
@ -83,16 +78,16 @@ class Task:
|
||||
|
||||
Task._check_output(
|
||||
*command,
|
||||
exception=BuildFailed(self.package.base),
|
||||
exception=BuildError(self.package.base),
|
||||
cwd=sources_dir,
|
||||
logger=self.build_logger,
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
|
||||
# well it is not actually correct, but we can deal with it
|
||||
packages = Task._check_output("makepkg", "--packagelist",
|
||||
exception=BuildFailed(self.package.base),
|
||||
exception=BuildError(self.package.base),
|
||||
cwd=sources_dir,
|
||||
logger=self.build_logger).splitlines()
|
||||
logger=self.logger).splitlines()
|
||||
return [Path(package) for package in packages]
|
||||
|
||||
def init(self, sources_dir: Path, database: SQLite) -> None:
|
||||
|
@ -27,7 +27,7 @@ from logging.config import fileConfig
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Generator, List, Optional, Tuple, Type
|
||||
|
||||
from ahriman.core.exceptions import InitializeException
|
||||
from ahriman.core.exceptions import InitializeError
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@ -70,11 +70,15 @@ class Configuration(configparser.RawConfigParser):
|
||||
ARCHITECTURE_SPECIFIC_SECTIONS = ["build", "sign", "web"]
|
||||
SYSTEM_CONFIGURATION_PATH = Path(sys.prefix) / "share" / "ahriman" / "settings" / "ahriman.ini"
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, allow_no_value: bool = False) -> None:
|
||||
"""
|
||||
default constructor. In the most cases must not be called directly
|
||||
|
||||
Args:
|
||||
allow_no_value(bool): copies ``configparser.RawConfigParser`` behaviour. In case if it is set to ``True``,
|
||||
the keys without values will be allowed
|
||||
"""
|
||||
configparser.RawConfigParser.__init__(self, allow_no_value=True, converters={
|
||||
configparser.RawConfigParser.__init__(self, allow_no_value=allow_no_value, converters={
|
||||
"list": self.__convert_list,
|
||||
"path": self.__convert_path,
|
||||
})
|
||||
@ -204,7 +208,7 @@ class Configuration(configparser.RawConfigParser):
|
||||
InitializeException: in case if architecture and/or path are not set
|
||||
"""
|
||||
if self.path is None or self.architecture is None:
|
||||
raise InitializeException("Configuration path and/or architecture are not set")
|
||||
raise InitializeError("Configuration path and/or architecture are not set")
|
||||
return self.path, self.architecture
|
||||
|
||||
def dump(self) -> Dict[str, Dict[str, str]]:
|
||||
@ -220,15 +224,15 @@ class Configuration(configparser.RawConfigParser):
|
||||
}
|
||||
|
||||
# pylint and mypy are too stupid to find these methods
|
||||
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument,no-self-use
|
||||
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ...
|
||||
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
|
||||
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ... # type: ignore
|
||||
|
||||
def getpath(self, *args: Any, **kwargs: Any) -> Path: ...
|
||||
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore
|
||||
|
||||
def gettype(self, section: str, architecture: str) -> Tuple[str, str]:
|
||||
"""
|
||||
get type variable with fallback to old logic
|
||||
Despite the fact that it has same semantics as other get* methods, but it has different argument list
|
||||
get type variable with fallback to old logic. Despite the fact that it has same semantics as other get* methods,
|
||||
but it has different argument list
|
||||
|
||||
Args:
|
||||
section(str): section name
|
||||
|
@ -36,7 +36,7 @@ def migrate_package_remotes(connection: Connection, paths: RepositoryPaths) -> N
|
||||
connection(Connection): database connection
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
"""
|
||||
from ahriman.core.database.operations.package_operations import PackageOperations
|
||||
from ahriman.core.database.operations import PackageOperations
|
||||
|
||||
def insert_remote(base: str, remote: RemoteSource) -> None:
|
||||
connection.execute(
|
||||
|
@ -19,8 +19,6 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
from pkgutil import iter_modules
|
||||
@ -29,11 +27,12 @@ from typing import List, Type
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database.data import migrate_data
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.models.migration import Migration
|
||||
from ahriman.models.migration_result import MigrationResult
|
||||
|
||||
|
||||
class Migrations:
|
||||
class Migrations(LazyLogging):
|
||||
"""
|
||||
simple migration wrapper for the sqlite
|
||||
idea comes from https://www.ash.dev/blog/simple-migration-system-in-sqlite/
|
||||
@ -41,7 +40,6 @@ class Migrations:
|
||||
Attributes:
|
||||
configuration(Configuration): configuration instance
|
||||
connection(Connection): database connection
|
||||
logger(logging.Logger): class logger
|
||||
"""
|
||||
|
||||
def __init__(self, connection: Connection, configuration: Configuration) -> None:
|
||||
@ -54,7 +52,6 @@ class Migrations:
|
||||
"""
|
||||
self.connection = connection
|
||||
self.configuration = configuration
|
||||
self.logger = logging.getLogger("database")
|
||||
|
||||
@classmethod
|
||||
def migrate(cls: Type[Migrations], connection: Connection, configuration: Configuration) -> MigrationResult:
|
||||
@ -86,7 +83,7 @@ class Migrations:
|
||||
module = import_module(f"{__name__}.{module_name}")
|
||||
steps: List[str] = getattr(module, "steps", [])
|
||||
self.logger.debug("found migration %s at index %s with steps count %s", module_name, index, len(steps))
|
||||
migrations.append(Migration(index, module_name, steps))
|
||||
migrations.append(Migration(index=index, name=module_name, steps=steps))
|
||||
|
||||
return migrations
|
||||
|
||||
@ -100,7 +97,7 @@ class Migrations:
|
||||
migrations = self.migrations()
|
||||
current_version = self.user_version()
|
||||
expected_version = len(migrations)
|
||||
result = MigrationResult(current_version, expected_version)
|
||||
result = MigrationResult(old_version=current_version, new_version=expected_version)
|
||||
|
||||
if not result.is_outdated:
|
||||
self.logger.info("no migrations required")
|
||||
|
33
src/ahriman/core/database/migrations/m002_user_access.py
Normal file
33
src/ahriman/core/database/migrations/m002_user_access.py
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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/>.
|
||||
#
|
||||
__all__ = ["steps"]
|
||||
|
||||
|
||||
steps = [
|
||||
"""
|
||||
update users set access = 'read' where access = 'safe'
|
||||
""",
|
||||
"""
|
||||
update users set access = 'reporter' where access = 'read'
|
||||
""",
|
||||
"""
|
||||
update users set access = 'full' where access = 'write'
|
||||
""",
|
||||
]
|
43
src/ahriman/core/database/migrations/m003_patch_variables.py
Normal file
43
src/ahriman/core/database/migrations/m003_patch_variables.py
Normal file
@ -0,0 +1,43 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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/>.
|
||||
#
|
||||
__all__ = ["steps"]
|
||||
|
||||
|
||||
steps = [
|
||||
"""
|
||||
alter table patches rename to patches_
|
||||
""",
|
||||
"""
|
||||
create table patches (
|
||||
package_base text not null,
|
||||
variable text,
|
||||
patch blob not null
|
||||
)
|
||||
""",
|
||||
"""
|
||||
create unique index patches_package_base_variable on patches (package_base, coalesce(variable, ''))
|
||||
""",
|
||||
"""
|
||||
insert into patches (package_base, patch) select package_base, patch from patches_
|
||||
""",
|
||||
"""
|
||||
drop table patches_
|
||||
""",
|
||||
]
|
@ -17,3 +17,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman.core.database.operations.operations import Operations
|
||||
|
||||
from ahriman.core.database.operations.auth_operations import AuthOperations
|
||||
from ahriman.core.database.operations.build_operations import BuildOperations
|
||||
from ahriman.core.database.operations.package_operations import PackageOperations
|
||||
from ahriman.core.database.operations.patch_operations import PatchOperations
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user