Compare commits

..

1 Commits

Author SHA1 Message Date
ead54be367 Release 0.9.0 2021-03-08 15:59:50 +03:00
401 changed files with 1200 additions and 37571 deletions

View File

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

View File

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

View File

@ -1,24 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Summary
A clear and concise description of what the bug is.
### Steps to Reproduce
Steps to reproduce the behavior (commands, environment etc)
### Expected behavior
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.

View File

@ -1 +0,0 @@
blank_issues_enabled: false

View File

@ -1,12 +0,0 @@
---
name: Question
about: Create an issue to get help with project
title: ''
labels: question
assignees: ''
---
## Describe your question below
A clear and concise description of your issue for which you would like to get help.

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
## Feature summary
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 [...]
### Proposed changes and/or features
A clear and concise description of what you want to happen.

View File

@ -1,13 +0,0 @@
## Summary
Brief description of the pull request. Try to provide clear explanation for major changes.
Please make sure that branch called either `feature/feature-name` for feature-related pull requests or `bug/bug-name` for bug-related ones.
Put `closes #ISSUE` in case if the pull requests solves one of the opened issues.
### Checklist
- [ ] Tests to cover new code
- [ ] `make check` passed
- [ ] `make tests` passed

View File

@ -1,42 +0,0 @@
name: docker image
on:
push:
branches: [ master ]
tags:
- '*'
- '!*rc*'
jobs:
docker-image:
runs-on: ubuntu-latest
steps:
- name: extract docker metadata
id: meta
uses: docker/metadata-action@v3
with:
images: |
arcan1s/ahriman
tags: |
type=ref,event=tag
type=edge
- name: setup QEMU
uses: docker/setup-qemu-action@v1
- name: setup docker buildx
uses: docker/setup-buildx-action@v1
- name: login to docker hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: build an image and push
uses: docker/build-push-action@v2
with:
push: true
tags: ${{ steps.meta.outputs.tags }}

View File

@ -1,41 +0,0 @@
name: release
on:
push:
tags:
- '*'
jobs:
make-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: extract version
id: version
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
- name: create changelog
id: changelog
uses: jaywcjlove/changelog-generator@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
filter: 'Release \d+\.\d+\.\d+'
- name: create archive
run: make archive
env:
VERSION: ${{ steps.version.outputs.VERSION }}
- name: release
uses: softprops/action-gh-release@v1
with:
body: |
${{ steps.changelog.outputs.compareurl }}
${{ steps.changelog.outputs.changelog }}
files: ahriman-*-src.tar.xz
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,24 +0,0 @@
name: setup
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
run-setup:
runs-on: ubuntu-latest
container:
image: archlinux:latest
volumes:
- ${{ github.workspace }}:/build
options: --privileged -w /build
steps:
- uses: actions/checkout@v2
- name: setup the service in arch linux container
run: .github/workflows/setup.sh

View File

@ -1,24 +0,0 @@
name: tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
run-tests:
runs-on: ubuntu-latest
container:
image: archlinux:latest
volumes:
- ${{ github.workspace }}:/build
options: -w /build
steps:
- uses: actions/checkout@v2
- name: run check and tests in arch linux container
run: .github/workflows/tests.sh

View File

@ -1,52 +0,0 @@
#!/bin/bash
# Install the package and run main install commands
set -ex
# 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
# 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
# create fresh tarball
make VERSION=1.0.0 archlinux # well, it does not really matter which version we will put here
# run makepkg
mv ahriman-*-src.tar.xz package/archlinux
chmod +777 package/archlinux # because fuck you that's why
cd package/archlinux
sudo -u nobody -- makepkg -cf --skipchecksums --noconfirm
pacman --noconfirm -U ahriman-1.0.0-1-any.pkg.tar.zst
# create machine-id which is required by build tools
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
# 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
# add the first package
# the build itself does not really work in the container
sudo -u ahriman -- ahriman package-add --now yay
# check if package was actually installed
#test -n "$(find "/var/lib/ahriman/repository/x86_64" -name "yay*pkg*")"
# run package check
sudo -u ahriman -- ahriman repo-update
# stop web service lol
kill $WEBPID

View File

@ -1,10 +0,0 @@
#!/bin/bash
# Install dependencies and run test in container
set -ex
# install dependencies
pacman --noconfirm -Syu base-devel python-pip python-tox
# run test and check targets
make check tests

5
.gitignore vendored
View File

@ -94,8 +94,3 @@ ENV/
.venv/
*.tar.xz
status_cache.json
*.db
docs/html/

607
.pylintrc
View File

@ -1,607 +0,0 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=
# Specify a score threshold to be exceeded before program exits with error.
fail-under=10.0
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use.
jobs=0
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --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,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
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,
import-outside-toplevel,
invalid-name,
raise-missing-from,
wrong-import-order,
too-few-public-methods,
too-many-instance-attributes,
broad-except,
too-many-ancestors,
fixme,
too-many-arguments,
duplicate-code,
cyclic-import
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[REPORTS]
# Python expression which should return a score less than or equal to 10. You
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
# which contain the number of messages in each category, as well as 'statement'
# which is the total number of statements analyzed. This score is used by the
# global evaluation report (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style.
#class-attribute-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style.
#variable-rgx=
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
# List of decorators that change the signature of a decorated function.
signature-mutators=
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[LOGGING]
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
# Regular expression of note tags to take in consideration.
#notes-rgx=
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work,
# install the python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains the private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to the private dictionary (see the
# --spelling-private-dict-file option) instead of raising a message.
spelling-store-unknown-words=no
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore.
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled).
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled).
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[DESIGN]
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp,
__post_init__
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception

View File

@ -1,20 +0,0 @@
version: 2
formats: all
build:
os: ubuntu-20.04
tools:
python: "3.9"
sphinx:
builder: html
configuration: docs/conf.py
python:
install:
- method: pip
path: .
extra_requirements:
- docs
system_packages: true

View File

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

73
CONFIGURING.md Normal file
View File

@ -0,0 +1,73 @@
# ahriman configuration
Some groups can be specified for each architecture separately with default values. E.g. if there are `build` and `build_x86_64` groups it will use the `build_x86_64` for the `x86_64` architecture and `build` for any other.
## `settings` group
Base configuration settings:
* `include` - path to directory with configuration files overrides, string, required.
* `logging` - path to logging configuration, string, required. Check `logging.ini` for reference.
## `aur` group
AUR related configuration:
* `url` - base url for AUR, string, required.
## `build_*` groups
Build related configuration. Group name must refer to architecture, e.g. it should be `build_x86_64` for x86_64 architecture.
* `archbuild_flags` - additional flags passed to `archbuild` command, space separated list of strings, optional.
* `build_command` - default build command, string, required.
* `ignore_packages` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional.
* `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.
## `repository` group
Base repository settings:
* `name` - repository name, string, required.
* `root` - root path for application, string, required.
## `sign` group
Settings for signing packages or repository:
* `enabled` - configuration flag to enable signing, string, required. Allowed values are `disabled`, `package` (sign each package separately), `repository` (sign repository database file).
* `key` - PGP key, string, optional.
## `report` group
Report generation settings:
* `target` - list of reports to be generated, space separated list of strings, optional. Allowed values are `html`.
### `html_*` group
Group name must refer to architecture, e.g. it should be `html_x86_64` for x86_64 architecture.
* `path` - path to html report file, string, required.
* `homepage` - link to homepage, string, optional.
* `link_path` - prefix for HTML links, string, required.
* `template_path` - path to Jinja2 template, string, required.
## `upload` group
Remote synchronization settings:
* `target` - list of synchronizations to be used, space separated list of strings, optional. Allowed values are `rsync`, `s3`.
### `s3_*` group
Group name must refer to architecture, e.g. it should be `s3_x86_64` for x86_64 architecture. Requires `aws-cli` package to be installed. Do not forget to configure it for user `ahriman`.
* `bucket` - bucket name (e.g. `s3://bucket/path`), string, required.
### `rsync_*` group
Group name must refer to architecture, e.g. it should be `rsync_x86_64` for x86_64 architecture. Requires `rsync` package to be installed. Do not forget to configure ssh for user `ahriman`.
* `remote` - remote server to rsync (e.g. `1.2.3.4:5678:path/to/sync`), string, required.

View File

@ -1,52 +0,0 @@
FROM archlinux:base-devel
# image configuration
ENV AHRIMAN_ARCHITECTURE="x86_64"
ENV AHRIMAN_DEBUG=""
ENV AHRIMAN_FORCE_ROOT=""
ENV AHRIMAN_HOST="0.0.0.0"
ENV AHRIMAN_OUTPUT="syslog"
ENV AHRIMAN_PACKAGER="ahriman bot <ahriman@example.com>"
ENV AHRIMAN_PORT=""
ENV AHRIMAN_REPOSITORY="aur-clone"
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
## 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"
## 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
# install ahriman
## copy tree
COPY --chown=build . "/home/build/ahriman"
## create package archive and install it
RUN cd "/home/build/ahriman" && \
make VERSION=$(python -c "from src.ahriman.version import __version__; print(__version__)") archlinux && \
cp ./*-src.tar.xz "package/archlinux" && \
cd "package/archlinux" && \
runuser -u build -- makepkg --noconfirm --install --skipchecksums && \
cd - && rm -r "/home/build/ahriman"
VOLUME ["/var/lib/ahriman"]
# minimal runtime ahriman setup
COPY "docker/entrypoint.sh" "/usr/local/bin/entrypoint"
ENTRYPOINT ["entrypoint"]
# default command
CMD ["repo-update"]

View File

@ -1,59 +0,0 @@
.PHONY: archive archive_directory archlinux check clean directory push spec spec-html tests version
.DEFAULT_GOAL := archlinux
PROJECT := ahriman
FILES := AUTHORS COPYING README.md docs package src setup.py tox.ini web.png
TARGET_FILES := $(addprefix $(PROJECT)/, $(FILES))
IGNORE_FILES := package/archlinux src/.mypy_cache
$(TARGET_FILES) : $(addprefix $(PROJECT), %) : $(addprefix ., %) directory version
@cp -rp $< $@
archive: archive_directory
tar cJf "$(PROJECT)-$(VERSION)-src.tar.xz" "$(PROJECT)"
rm -rf "$(PROJECT)"
archive_directory: $(TARGET_FILES)
rm -fr $(addprefix $(PROJECT)/, $(IGNORE_FILES))
find "$(PROJECT)" -type f -name "*.pyc" -delete
find "$(PROJECT)" -depth -type d -name "__pycache__" -execdir rm -rf {} +
find "$(PROJECT)" -depth -type d -name "*.egg-info" -execdir rm -rf {} +
archlinux: archive
sed -i "s/pkgver=.*/pkgver=$(VERSION)/" package/archlinux/PKGBUILD
check: clean
tox -e check
clean:
find . -type f -name "$(PROJECT)-*-src.tar.xz" -delete
rm -rf "$(PROJECT)"
directory: clean
mkdir "$(PROJECT)"
push: spec 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:
# 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
version:
ifndef VERSION
$(error VERSION is required, but not set)
endif
sed -i '/__version__ = .*/s/[^"][^)]*/__version__ = "$(VERSION)"/' src/ahriman/version.py

View File

@ -1,32 +1,21 @@
# ArcH linux ReposItory MANager
[![tests status](https://github.com/arcan1s/ahriman/actions/workflows/run-tests.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/run-tests.yml)
[![setup status](https://github.com/arcan1s/ahriman/actions/workflows/run-setup.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/run-setup.yml)
[![docker image](https://github.com/arcan1s/ahriman/actions/workflows/docker-image.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/docker-image.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/arcan1s/ahriman/badge)](https://www.codefactor.io/repository/github/arcan1s/ahriman)
[![Documentation Status](https://readthedocs.org/projects/ahriman/badge/?version=latest)](https://ahriman.readthedocs.io/?badge=latest)
# ArcHlinux ReposItory MANager
Wrapper for managing custom repository inspired by [repo-scripts](https://github.com/arcan1s/repo-scripts).
## Features
* Install-configure-forget manager for 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).
* Dependency manager.
* Ability to patch AUR packages and even create package from local PKGBUILDs.
* Repository status interface with optional authorization and control options:
![web interface](web.png)
## 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).
* Install package as usual.
* Change settings if required, see `CONFIGURING.md` for more details.
* Create `/var/lib/ahriman/.makepkg.conf` with `makepkg.conf` overrides if required (at least you might want to set `PACKAGER`).
* Configure build tools (it might be required if your package will use any custom repositories):
* create build command if required, e.g. `ln -s /usr/bin/archbuild /usr/local/bin/custom-x86_64-build` (you can choose any name for command);
* create configuration file, e.g. `cp /usr/share/devtools/pacman-{extra,custom}.conf`;
* change configuration file: add your own repository, add multilib repository;
* set `build.build_command` to point to your command;
* configure `/etc/sudoers.d/ahriman` to allow to run command without password.
* Start and enable `ahriman.timer` via `systemctl`.
* Add packages by using `ahriman add {package}` command.
## Configuration
## Limitations
Every available option is described in the [documentation](docs/configuration.rst).
## [FAQ](docs/faq.rst)
* It does not manage dependencies, so you have to add them before main package.

View File

@ -1,49 +0,0 @@
#!/bin/bash
set -e
[ -n "$AHRIMAN_DEBUG" ] && set -x
# configuration tune
sed -i "s|root = /var/lib/ahriman|root = $AHRIMAN_REPOSITORY_ROOT|g" "/etc/ahriman.ini"
sed -i "s|database = /var/lib/ahriman/ahriman.db|database = $AHRIMAN_REPOSITORY_ROOT/ahriman.db|g" "/etc/ahriman.ini"
sed -i "s|host = 127.0.0.1|host = $AHRIMAN_HOST|g" "/etc/ahriman.ini"
sed -i "s|handlers = syslog_handler|handlers = ${AHRIMAN_OUTPUT}_handler|g" "/etc/ahriman.ini.d/logging.ini"
AHRIMAN_DEFAULT_ARGS=("--architecture" "$AHRIMAN_ARCHITECTURE")
if [[ "$AHRIMAN_OUTPUT" == "syslog" ]]; then
if [ ! -e "/dev/log" ]; then
# by default ahriman uses syslog which is not available inside container
# to make noise less we force quiet mode in case if /dev/log was not mounted
AHRIMAN_DEFAULT_ARGS+=("--quiet")
fi
fi
# create repository root inside the [[mounted]] directory and set correct ownership
[ -d "$AHRIMAN_REPOSITORY_ROOT" ] || mkdir "$AHRIMAN_REPOSITORY_ROOT"
chown "$AHRIMAN_USER":"$AHRIMAN_USER" "$AHRIMAN_REPOSITORY_ROOT"
# run built-in setup command
AHRIMAN_SETUP_ARGS=("--build-as-user" "$AHRIMAN_USER")
AHRIMAN_SETUP_ARGS+=("--packager" "$AHRIMAN_PACKAGER")
AHRIMAN_SETUP_ARGS+=("--repository" "$AHRIMAN_REPOSITORY")
if [ -n "$AHRIMAN_PORT" ]; then
# in addition it must be handled in docker run command
AHRIMAN_SETUP_ARGS+=("--web-port" "$AHRIMAN_PORT")
fi
ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" repo-setup "${AHRIMAN_SETUP_ARGS[@]}"
# refresh database
runuser -u build -- yay --noconfirm -Syy &> /dev/null
# create machine-id which is required by build tools
systemd-machine-id-setup &> /dev/null
# if AHRIMAN_FORCE_ROOT is set or command is unsafe we can run without sudo
# otherwise we prepend executable by sudo command
if [ -n "$AHRIMAN_FORCE_ROOT" ]; then
AHRIMAN_EXECUTABLE=("ahriman")
elif ahriman help-commands-unsafe --command="$*" &> /dev/null; then
AHRIMAN_EXECUTABLE=("sudo" "-u" "$AHRIMAN_USER" "--" "ahriman")
else
AHRIMAN_EXECUTABLE=("ahriman")
fi
exec "${AHRIMAN_EXECUTABLE[@]}" "${AHRIMAN_DEFAULT_ARGS[@]}" "$@"

View File

@ -1,43 +0,0 @@
Advanced usage
==============
Depending on the goal the package can be used in different ways. Nevertheless, in the most cases you will need some basic classes
.. code-block:: python
from pathlib import Path
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
architecture = "x86_64"
configuration = Configuration.from_path(Path("/etc/ahriman.ini"), architecture, quiet=False)
sqlite = SQLite.load(configuration)
At this point there are ``configuration`` and ``database`` instances which can be used later at any time anywhere, e.g.
.. code-block:: python
# instance of ``RepositoryPaths`` class
paths = configuration.repository_paths
Almost all actions are wrapped by ``ahriman.core.repository.Repository`` class
.. code-block:: python
from ahriman.core.repository import Repository
repository = Repository(architecture, configuration, database, no_report=False, unsafe=False)
And the ``repository`` instance can be used to perform repository maintenance
.. code-block:: python
build_result = repository.process_build(known_packages)
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)
For the more info please refer to the classes documentation.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 503 KiB

View File

@ -1,621 +0,0 @@
.TH AHRIMAN "1" 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-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-update,update,user-add,user-list,user-remove,web} ...
.SH DESCRIPTION
ArcH Linux ReposItory MANager
.SH OPTIONS
.TP
\fB\-a\fR \fI\,ARCHITECTURE\/\fR, \fB\-\-architecture\fR \fI\,ARCHITECTURE\/\fR
target architectures (can be used multiple times)
.TP
\fB\-c\fR \fI\,CONFIGURATION\/\fR, \fB\-\-configuration\fR \fI\,CONFIGURATION\/\fR
configuration path
.TP
\fB\-\-force\fR
force run, remove file lock
.TP
\fB\-l\fR \fI\,LOCK\/\fR, \fB\-\-lock\fR \fI\,LOCK\/\fR
lock file
.TP
\fB\-\-no\-report\fR
force disable reporting to web service
.TP
\fB\-q\fR, \fB\-\-quiet\fR
force disable any logging
.TP
\fB\-\-unsafe\fR
allow to run ahriman as non\-ahriman user. Some actions might be unavailable
.TP
\fB\-v\fR, \fB\-\-version\fR
show program's version number and exit
.SH
COMMAND
.TP
\fBahriman\fR \fI\,aur-search\/\fR
search for package
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help-commands-unsafe\/\fR
list unsafe commands
.TP
\fBahriman\fR \fI\,key-import\/\fR
import PGP key
.TP
\fBahriman\fR \fI\,package-add\/\fR
add package
.TP
\fBahriman\fR \fI\,package-remove\/\fR
remove package
.TP
\fBahriman\fR \fI\,package-status\/\fR
get package status
.TP
\fBahriman\fR \fI\,package-status-remove\/\fR
remove package status
.TP
\fBahriman\fR \fI\,package-status-update\/\fR
update package status
.TP
\fBahriman\fR \fI\,patch-add\/\fR
add patch set
.TP
\fBahriman\fR \fI\,patch-list\/\fR
list patch sets
.TP
\fBahriman\fR \fI\,patch-remove\/\fR
remove patch set
.TP
\fBahriman\fR \fI\,repo-backup\/\fR
backup repository data
.TP
\fBahriman\fR \fI\,repo-check\/\fR
check for updates
.TP
\fBahriman\fR \fI\,repo-clean\/\fR
clean local caches
.TP
\fBahriman\fR \fI\,repo-config\/\fR
dump configuration
.TP
\fBahriman\fR \fI\,repo-rebuild\/\fR
rebuild repository
.TP
\fBahriman\fR \fI\,repo-remove-unknown\/\fR
remove unknown packages
.TP
\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
initial service configuration
.TP
\fBahriman\fR \fI\,repo-sign\/\fR
sign packages
.TP
\fBahriman\fR \fI\,repo-status-update\/\fR
update repository status
.TP
\fBahriman\fR \fI\,repo-sync\/\fR
sync repository
.TP
\fBahriman\fR \fI\,repo-update\/\fR
update packages
.TP
\fBahriman\fR \fI\,user-add\/\fR
create or update user
.TP
\fBahriman\fR \fI\,user-list\/\fR
user known users and their access
.TP
\fBahriman\fR \fI\,user-remove\/\fR
remove user
.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}]
search [search ...]
search for package in AUR using API
.TP
\fBsearch\fR
search terms, can be specified multiple times, result will match all terms
.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
.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 help'\/\fR
usage: ahriman help [-h] [command]
show help message for application or command and exit
.TP
\fBcommand\fR
show help message for specific command
.SH COMMAND \fI\,'ahriman help-commands-unsafe'\/\fR
usage: ahriman help-commands-unsafe [-h] [--command COMMAND]
list unsafe commands as defined in default args
.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
import PGP key from public sources to the repository user
.TP
\fBkey\fR
PGP key to import from public server
.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]
package [package ...]
add existing or new package to the build queue
.TP
\fBpackage\fR
package source (base name, path to local files, remote URL)
.SH OPTIONS \fI\,'ahriman package-add'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-n\fR, \fB\-\-now\fR
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
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 ...]
remove package from the repository
.TP
\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 ...]
request status of the package
.TP
\fBpackage\fR
filter status by package base
.SH OPTIONS \fI\,'ahriman package-status'\/\fR
.TP
\fB\-\-ahriman\fR
get service status itself
.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
.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
filter packages by status
.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
.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 ...]
update package status on the status page
.TP
\fBpackage\fR
set status for specified packages. If no packages supplied, service status will be updated
.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
.SH COMMAND \fI\,'ahriman patch-add'\/\fR
usage: ahriman patch-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-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 patch-list'\/\fR
usage: ahriman patch-list [-h] [-e] [package]
list available patches for the package
.TP
\fBpackage\fR
package base
.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
remove patches for the package
.TP
\fBpackage\fR
package base
.SH COMMAND \fI\,'ahriman repo-backup'\/\fR
usage: ahriman repo-backup [-h] path
backup 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 ...]
check for packages updates. Same as update \-\-dry\-run \-\-no\-manual
.TP
\fBpackage\fR
filter check by package base
.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
.SH COMMAND \fI\,'ahriman repo-clean'\/\fR
usage: ahriman repo-clean [-h] [--cache] [--chroot] [--manual] [--packages]
remove local caches
.SH OPTIONS \fI\,'ahriman repo-clean'\/\fR
.TP
\fB\-\-cache\fR
clear directory with package caches
.TP
\fB\-\-chroot\fR
clear build chroot
.TP
\fB\-\-manual\fR
clear manually added packages queue
.TP
\fB\-\-packages\fR
clear directory with built packages
.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]
force rebuild whole repository
.SH OPTIONS \fI\,'ahriman repo-rebuild'\/\fR
.TP
\fB\-\-depends\-on\fR \fI\,DEPENDS_ON\/\fR
only rebuild packages that depend on specified package
.TP
\fB\-\-dry\-run\fR
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
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]
remove packages which are missing in AUR and do not have local PKGBUILDs
.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] [target ...]
generate repository report according to current settings
.TP
\fBtarget\fR
target to generate report
.SH COMMAND \fI\,'ahriman repo-restore'\/\fR
usage: ahriman repo-restore [-h] [-o OUTPUT] path
restore settings and database
.TP
\fBpath\fR
path of the input archive
.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]
create initial service configuration, requires root
.SH OPTIONS \fI\,'ahriman repo-setup'\/\fR
.TP
\fB\-\-build\-as\-user\fR \fI\,BUILD_AS_USER\/\fR
force makepkg user to the specific one
.TP
\fB\-\-build\-command\fR \fI\,BUILD_COMMAND\/\fR
build command prefix
.TP
\fB\-\-from\-configuration\fR \fI\,FROM_CONFIGURATION\/\fR
path to default devtools pacman configuration
.TP
\fB\-\-no\-multilib\fR
do not add multilib repository
.TP
\fB\-\-packager\fR \fI\,PACKAGER\/\fR
packager name and email
.TP
\fB\-\-repository\fR \fI\,REPOSITORY\/\fR
repository name
.TP
\fB\-\-sign\-key\fR \fI\,SIGN_KEY\/\fR
sign key id
.TP
\fB\-\-sign\-target\fR \fI\,{SignSettings.Disabled,SignSettings.Packages,SignSettings.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 ...]
(re\-)sign packages and repository database according to current settings
.TP
\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}]
update repository status on the status page
.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
new status
.SH COMMAND \fI\,'ahriman repo-sync'\/\fR
usage: ahriman repo-sync [-h] [target ...]
sync repository files to remote server according to current settings
.TP
\fBtarget\fR
target to sync
.SH COMMAND \fI\,'ahriman repo-update'\/\fR
usage: ahriman repo-update [-h] [--dry-run] [-e] [--no-aur] [--no-local] [--no-manual] [--no-vcs] [package ...]
check for packages updates and run build process if requested
.TP
\fBpackage\fR
filter check by package base
.SH OPTIONS \fI\,'ahriman repo-update'\/\fR
.TP
\fB\-\-dry\-run\fR
just perform check for updates, same as check command
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-\-no\-aur\fR
do not check for AUR updates. Implies \-\-no\-vcs
.TP
\fB\-\-no\-local\fR
do not check local packages for updates
.TP
\fB\-\-no\-manual\fR
do not include manual updates
.TP
\fB\-\-no\-vcs\fR
do not check VCS packages
.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
update user for web services with the given password and role. In case if password was not entered it will be asked interactively
.TP
\fBusername\fR
username for web service
.SH OPTIONS \fI\,'ahriman user-add'\/\fR
.TP
\fB\-\-as\-service\fR
add user as service user
.TP
\fB\-p\fR \fI\,PASSWORD\/\fR, \fB\-\-password\fR \fI\,PASSWORD\/\fR
user password. Blank password will be treated as empty password, which is in particular must be used for OAuth2
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
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]
list users from the user mapping and their roles
.TP
\fBusername\fR
filter users by username
.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
filter users by role
.SH COMMAND \fI\,'ahriman user-remove'\/\fR
usage: ahriman user-remove [-h] [-s] username
remove user from the user mapping and update the configuration
.TP
\fBusername\fR
username for web service
.SH OPTIONS \fI\,'ahriman user-remove'\/\fR
.TP
\fB\-s\fR, \fB\-\-secure\fR
set file permissions to user\-only
.SH COMMAND \fI\,'ahriman web'\/\fR
usage: ahriman web [-h]
start web server
.SH COMMENTS
Argument list can also be read from file by using @ prefix.
.SH AUTHORS
.nf
ahriman team
.fi.nf
.fi
.SH DISTRIBUTION
The latest version of ahriman may be downloaded from
.UR https://github.com/arcan1s/ahriman
.UE

View File

@ -1,45 +0,0 @@
ahriman.application.application package
=======================================
Submodules
----------
ahriman.application.application.application module
--------------------------------------------------
.. automodule:: ahriman.application.application.application
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.application.application\_packages module
------------------------------------------------------------
.. automodule:: ahriman.application.application.application_packages
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.application.application\_properties module
--------------------------------------------------------------
.. automodule:: ahriman.application.application.application_properties
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.application.application\_repository module
--------------------------------------------------------------
.. automodule:: ahriman.application.application.application_repository
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.application.application
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,197 +0,0 @@
ahriman.application.handlers package
====================================
Submodules
----------
ahriman.application.handlers.add module
---------------------------------------
.. automodule:: ahriman.application.handlers.add
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.backup module
------------------------------------------
.. automodule:: ahriman.application.handlers.backup
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.clean module
-----------------------------------------
.. automodule:: ahriman.application.handlers.clean
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.dump module
----------------------------------------
.. automodule:: ahriman.application.handlers.dump
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.handler module
-------------------------------------------
.. automodule:: ahriman.application.handlers.handler
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.help module
----------------------------------------
.. automodule:: ahriman.application.handlers.help
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.key\_import module
-----------------------------------------------
.. automodule:: ahriman.application.handlers.key_import
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.patch module
-----------------------------------------
.. automodule:: ahriman.application.handlers.patch
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.rebuild module
-------------------------------------------
.. automodule:: ahriman.application.handlers.rebuild
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.remove module
------------------------------------------
.. automodule:: ahriman.application.handlers.remove
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.remove\_unknown module
---------------------------------------------------
.. automodule:: ahriman.application.handlers.remove_unknown
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.report module
------------------------------------------
.. automodule:: ahriman.application.handlers.report
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.restore module
-------------------------------------------
.. automodule:: ahriman.application.handlers.restore
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.search module
------------------------------------------
.. automodule:: ahriman.application.handlers.search
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.setup module
-----------------------------------------
.. automodule:: ahriman.application.handlers.setup
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.sign module
----------------------------------------
.. automodule:: ahriman.application.handlers.sign
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.status module
------------------------------------------
.. automodule:: ahriman.application.handlers.status
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.status\_update module
--------------------------------------------------
.. automodule:: ahriman.application.handlers.status_update
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.sync module
----------------------------------------
.. automodule:: ahriman.application.handlers.sync
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.unsafe\_commands module
----------------------------------------------------
.. automodule:: ahriman.application.handlers.unsafe_commands
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.update module
------------------------------------------
.. automodule:: ahriman.application.handlers.update
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.users module
-----------------------------------------
.. automodule:: ahriman.application.handlers.users
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.web module
---------------------------------------
.. automodule:: ahriman.application.handlers.web
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.application.handlers
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,38 +0,0 @@
ahriman.application package
===========================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.application.application
ahriman.application.handlers
Submodules
----------
ahriman.application.ahriman module
----------------------------------
.. automodule:: ahriman.application.ahriman
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.lock module
-------------------------------
.. automodule:: ahriman.application.lock
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.application
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,45 +0,0 @@
ahriman.core.alpm.remote package
================================
Submodules
----------
ahriman.core.alpm.remote.aur module
-----------------------------------
.. automodule:: ahriman.core.alpm.remote.aur
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.alpm.remote.official module
----------------------------------------
.. automodule:: ahriman.core.alpm.remote.official
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.alpm.remote.official\_syncdb module
------------------------------------------------
.. automodule:: ahriman.core.alpm.remote.official_syncdb
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.alpm.remote.remote module
--------------------------------------
.. automodule:: ahriman.core.alpm.remote.remote
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.alpm.remote
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,37 +0,0 @@
ahriman.core.alpm package
=========================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.core.alpm.remote
Submodules
----------
ahriman.core.alpm.pacman module
-------------------------------
.. automodule:: ahriman.core.alpm.pacman
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.alpm.repo module
-----------------------------
.. automodule:: ahriman.core.alpm.repo
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.alpm
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,45 +0,0 @@
ahriman.core.auth package
=========================
Submodules
----------
ahriman.core.auth.auth module
-----------------------------
.. automodule:: ahriman.core.auth.auth
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.auth.helpers module
--------------------------------
.. automodule:: ahriman.core.auth.helpers
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.auth.mapping module
--------------------------------
.. automodule:: ahriman.core.auth.mapping
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.auth.oauth module
------------------------------
.. automodule:: ahriman.core.auth.oauth
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.auth
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,29 +0,0 @@
ahriman.core.build\_tools package
=================================
Submodules
----------
ahriman.core.build\_tools.sources module
----------------------------------------
.. automodule:: ahriman.core.build_tools.sources
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.build\_tools.task module
-------------------------------------
.. automodule:: ahriman.core.build_tools.task
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.build_tools
:members:
:no-undoc-members:
:show-inheritance:

View File

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

View File

@ -1,29 +0,0 @@
ahriman.core.database.migrations package
========================================
Submodules
----------
ahriman.core.database.migrations.m000\_initial module
-----------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m000_initial
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.migrations.m001\_package\_source module
-------------------------------------------------------------
.. automodule:: ahriman.core.database.migrations.m001_package_source
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.database.migrations
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,53 +0,0 @@
ahriman.core.database.operations package
========================================
Submodules
----------
ahriman.core.database.operations.auth\_operations module
--------------------------------------------------------
.. automodule:: ahriman.core.database.operations.auth_operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.build\_operations module
---------------------------------------------------------
.. automodule:: ahriman.core.database.operations.build_operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.operations module
--------------------------------------------------
.. automodule:: ahriman.core.database.operations.operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.package\_operations module
-----------------------------------------------------------
.. automodule:: ahriman.core.database.operations.package_operations
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.database.operations.patch\_operations module
---------------------------------------------------------
.. automodule:: ahriman.core.database.operations.patch_operations
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.database.operations
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,31 +0,0 @@
ahriman.core.database package
=============================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.core.database.data
ahriman.core.database.migrations
ahriman.core.database.operations
Submodules
----------
ahriman.core.database.sqlite module
-----------------------------------
.. automodule:: ahriman.core.database.sqlite
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.database
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,85 +0,0 @@
ahriman.core.formatters package
===============================
Submodules
----------
ahriman.core.formatters.aur\_printer module
-------------------------------------------
.. automodule:: ahriman.core.formatters.aur_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.build\_printer module
---------------------------------------------
.. automodule:: ahriman.core.formatters.build_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.configuration\_printer module
-----------------------------------------------------
.. automodule:: ahriman.core.formatters.configuration_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.package\_printer module
-----------------------------------------------
.. automodule:: ahriman.core.formatters.package_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.printer module
--------------------------------------
.. automodule:: ahriman.core.formatters.printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.status\_printer module
----------------------------------------------
.. automodule:: ahriman.core.formatters.status_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.string\_printer module
----------------------------------------------
.. automodule:: ahriman.core.formatters.string_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.update\_printer module
----------------------------------------------
.. automodule:: ahriman.core.formatters.update_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.user\_printer module
--------------------------------------------
.. automodule:: ahriman.core.formatters.user_printer
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.formatters
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,61 +0,0 @@
ahriman.core.report package
===========================
Submodules
----------
ahriman.core.report.console module
----------------------------------
.. automodule:: ahriman.core.report.console
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.report.email module
--------------------------------
.. automodule:: ahriman.core.report.email
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.report.html module
-------------------------------
.. automodule:: ahriman.core.report.html
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.report.jinja\_template module
------------------------------------------
.. automodule:: ahriman.core.report.jinja_template
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.report.report module
---------------------------------
.. automodule:: ahriman.core.report.report
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.report.telegram module
-----------------------------------
.. automodule:: ahriman.core.report.telegram
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.report
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,53 +0,0 @@
ahriman.core.repository package
===============================
Submodules
----------
ahriman.core.repository.cleaner module
--------------------------------------
.. automodule:: ahriman.core.repository.cleaner
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.repository.executor module
---------------------------------------
.. automodule:: ahriman.core.repository.executor
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.repository.repository module
-----------------------------------------
.. automodule:: ahriman.core.repository.repository
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.repository.repository\_properties module
-----------------------------------------------------
.. automodule:: ahriman.core.repository.repository_properties
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.repository.update\_handler module
----------------------------------------------
.. automodule:: ahriman.core.repository.update_handler
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.repository
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,70 +0,0 @@
ahriman.core package
====================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.core.alpm
ahriman.core.auth
ahriman.core.build_tools
ahriman.core.database
ahriman.core.formatters
ahriman.core.report
ahriman.core.repository
ahriman.core.sign
ahriman.core.status
ahriman.core.upload
Submodules
----------
ahriman.core.configuration module
---------------------------------
.. automodule:: ahriman.core.configuration
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.exceptions module
------------------------------
.. automodule:: ahriman.core.exceptions
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.spawn module
-------------------------
.. automodule:: ahriman.core.spawn
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.tree module
------------------------
.. automodule:: ahriman.core.tree
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.util module
------------------------
.. automodule:: ahriman.core.util
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,21 +0,0 @@
ahriman.core.sign package
=========================
Submodules
----------
ahriman.core.sign.gpg module
----------------------------
.. automodule:: ahriman.core.sign.gpg
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.sign
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,37 +0,0 @@
ahriman.core.status package
===========================
Submodules
----------
ahriman.core.status.client module
---------------------------------
.. automodule:: ahriman.core.status.client
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.status.watcher module
----------------------------------
.. automodule:: ahriman.core.status.watcher
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.status.web\_client module
--------------------------------------
.. automodule:: ahriman.core.status.web_client
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.status
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,53 +0,0 @@
ahriman.core.upload package
===========================
Submodules
----------
ahriman.core.upload.github module
---------------------------------
.. automodule:: ahriman.core.upload.github
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.upload.http\_upload module
---------------------------------------
.. automodule:: ahriman.core.upload.http_upload
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.upload.rsync module
--------------------------------
.. automodule:: ahriman.core.upload.rsync
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.upload.s3 module
-----------------------------
.. automodule:: ahriman.core.upload.s3
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.upload.upload module
---------------------------------
.. automodule:: ahriman.core.upload.upload
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.core.upload
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,189 +0,0 @@
ahriman.models package
======================
Submodules
----------
ahriman.models.action module
----------------------------
.. automodule:: ahriman.models.action
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.aur\_package module
----------------------------------
.. automodule:: ahriman.models.aur_package
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.auth\_settings module
------------------------------------
.. automodule:: ahriman.models.auth_settings
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.build\_status module
-----------------------------------
.. automodule:: ahriman.models.build_status
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.counters module
------------------------------
.. automodule:: ahriman.models.counters
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.internal\_status module
--------------------------------------
.. automodule:: ahriman.models.internal_status
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.migration module
-------------------------------
.. automodule:: ahriman.models.migration
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.migration\_result module
---------------------------------------
.. automodule:: ahriman.models.migration_result
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.package module
-----------------------------
.. automodule:: ahriman.models.package
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.package\_description module
------------------------------------------
.. automodule:: ahriman.models.package_description
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.package\_source module
-------------------------------------
.. automodule:: ahriman.models.package_source
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.property module
------------------------------
.. automodule:: ahriman.models.property
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.remote\_source module
------------------------------------
.. automodule:: ahriman.models.remote_source
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.report\_settings module
--------------------------------------
.. automodule:: ahriman.models.report_settings
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.repository\_paths module
---------------------------------------
.. automodule:: ahriman.models.repository_paths
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.result module
----------------------------
.. automodule:: ahriman.models.result
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.sign\_settings module
------------------------------------
.. automodule:: ahriman.models.sign_settings
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.smtp\_ssl\_settings module
-----------------------------------------
.. automodule:: ahriman.models.smtp_ssl_settings
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.upload\_settings module
--------------------------------------
.. automodule:: ahriman.models.upload_settings
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.user module
--------------------------
.. automodule:: ahriman.models.user
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.user\_access module
----------------------------------
.. automodule:: ahriman.models.user_access
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.user\_identity module
------------------------------------
.. automodule:: ahriman.models.user_identity
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.models
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,32 +0,0 @@
ahriman package
===============
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.application
ahriman.core
ahriman.models
ahriman.web
Submodules
----------
ahriman.version module
----------------------
.. automodule:: ahriman.version
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,29 +0,0 @@
ahriman.web.middlewares package
===============================
Submodules
----------
ahriman.web.middlewares.auth\_handler module
--------------------------------------------
.. automodule:: ahriman.web.middlewares.auth_handler
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.middlewares.exception\_handler module
-------------------------------------------------
.. automodule:: ahriman.web.middlewares.exception_handler
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.middlewares
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,38 +0,0 @@
ahriman.web package
===================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.web.middlewares
ahriman.web.views
Submodules
----------
ahriman.web.routes module
-------------------------
.. automodule:: ahriman.web.routes
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.web module
----------------------
.. automodule:: ahriman.web.web
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,39 +0,0 @@
ahriman.web.views package
=========================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ahriman.web.views.service
ahriman.web.views.status
ahriman.web.views.user
Submodules
----------
ahriman.web.views.base module
-----------------------------
.. automodule:: ahriman.web.views.base
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.index module
------------------------------
.. automodule:: ahriman.web.views.index
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,45 +0,0 @@
ahriman.web.views.service package
=================================
Submodules
----------
ahriman.web.views.service.add module
------------------------------------
.. automodule:: ahriman.web.views.service.add
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.remove module
---------------------------------------
.. automodule:: ahriman.web.views.service.remove
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.request module
----------------------------------------
.. automodule:: ahriman.web.views.service.request
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.service.search module
---------------------------------------
.. automodule:: ahriman.web.views.service.search
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.service
:members:
:no-undoc-members:
:show-inheritance:

View File

@ -1,45 +0,0 @@
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
---------------------------------------
.. automodule:: ahriman.web.views.status.package
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.status.packages module
----------------------------------------
.. automodule:: ahriman.web.views.status.packages
:members:
:no-undoc-members:
:show-inheritance:
ahriman.web.views.status.status module
--------------------------------------
.. automodule:: ahriman.web.views.status.status
:members:
:no-undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ahriman.web.views.status
:members:
:no-undoc-members:
:show-inheritance:

View File

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

View File

@ -1,261 +0,0 @@
Architecture
============
Package structure
-----------------
Packages have strict rules of importing:
* ``ahriman.application`` package must not be used anywhere except for itself.
* ``ahriman.core`` and ``ahriman.models`` packages don't have any import restriction. Actually we would like to totally restrict importing of ``core`` package from ``models``\ , but it is impossible at the moment.
* ``ahriman.web`` package is allowed to be imported from ``ahriman.application`` (web handler only, only ``ahriman.web.web`` methods). It also must not be imported globally, only local import is allowed.
Full dependency diagram:
.. image:: ahriman-architecture.svg
:target: _images/ahriman-architecture.svg
:alt: architecture
``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.
``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.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:
* ``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.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.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.
This package also provides some generic functions and classes which may be used by other packages:
* ``ahriman.core.configuration.Configuration`` is an extension for standard ``configparser`` library.
* ``ahriman.core.exceptions`` provides custom exceptions.
* ``ahriman.core.spawn.Spawn`` is a tool which can spawn another ``ahriman`` process. This feature is used by web application.
* ``ahriman.core.tree`` is a dependency tree implementation.
``ahriman.models`` package
^^^^^^^^^^^^^^^^^^^^^^^^^^
It provides models for any other part of application. Unlike ``ahriman.core`` package classes from here provides only conversion methods (e.g. create class from another or convert to). Mostly case classes and enumerations.
``ahriman.web`` package
^^^^^^^^^^^^^^^^^^^^^^^
Web application. It is important that this package is isolated from any other to allow it to be optional feature (i.e. dependencies which are required by the package are optional).
* ``ahriman.web.middlewares`` provides middlewares for request handlers.
* ``ahriman.web.views`` contains web views derived from aiohttp view class.
* ``ahriman.web.routes`` creates routes for web application.
* ``ahriman.web.web`` provides main web application functions (e.g. start, initialization).
Application run
---------------
* Parse command line arguments, find command and related handler which is set by parser.
* Call ``Handler.execute`` method.
* Define list of architectures to run. In case if there is more than one architecture specified run several subprocesses or process in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether application can be run in multiple processes or not - this feature is required for some handlers (e.g. ``Web``\ ) which should be able to spawn child process in daemon mode (it is impossible to do for daemonic processes).
* In each child process call lock functions.
* After success checks pass control to ``Handler.run`` method defined by specific handler class.
* Return result (success or failure) of each subprocess and exit from application.
* Some handlers may override their status and throw ``ExitCode`` exception. This exception is just silently suppressed and changes application exit code to ``1``.
In most cases handlers spawn god class ``ahriman.application.application.Application`` class and call required methods.
Application is designed to run from ``systemd`` services and provides parametrized by architecture timer and service file for that.
Database
--------
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).
The ``ahriman.core.database.sqlite.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.
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).
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.
Basic flows
-----------
Add new packages or rebuild existing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Idea is to copy package to the directory from which it will be handled at the next update run. Different variants are supported:
* 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.
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.
Rebuild packages
^^^^^^^^^^^^^^^^
Same as add function for every package in repository. Optional filter by reverse dependency can be supplied.
Remove packages
^^^^^^^^^^^^^^^
This flow removes package from filesystem, updates repository database and also runs synchronization and reporting methods.
Update packages
^^^^^^^^^^^^^^^
This feature is divided into to stages: check AUR for updates and run rebuild for required packages. Whereas check does not do anything except for check itself, update flow is the following:
#. Process every built package first. Those packages are usually added manually.
#. Run sync and report methods.
#. Generate dependency tree for packages to be built.
#. For each level of tree it does:
#. Download package data from AUR.
#. Build every package in clean chroot.
#. Sign packages if required.
#. Add packages to database and sign database if required.
#. Process sync and report methods.
After any step any package data is being removed.
Core functions reference
------------------------
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.
Utils
^^^^^
For every external command run (which is actually not recommended if possible) custom wrapper for ``subprocess`` is used. Additional functions ``ahriman.core.auth.helpers`` provide safe calls for ``aiohttp_security`` methods and are required to make this dependency optional.
Submodules
^^^^^^^^^^
Some packages provide different behaviour depending on configuration settings. In these cases inheritance is used and recommended way to deal with them is to call class method ``load`` from base classes.
Authorization
^^^^^^^^^^^^^
The package provides several authorization methods: disabled, based on configuration and OAuth2.
Disabled (default) authorization provider just allows everything for everyone and does not have any specific configuration (it uses some default configuration parameters though). It also provides generic interface for derived classes.
Mapping (aka configuration) provider uses hashed passwords with salt from the database in order to authenticate users. This provider also enables user permission checking (read/write) (authorization). Thus, it defines the following methods:
* ``check_credentials`` - user password validation (authentication).
* ``verify_access`` - user permission validation (authorization).
Passwords must be stored in database as ``hash(password + salt)``\ , where ``password`` is user defined password (taken from user input), ``salt`` is random string (any length) defined globally in configuration and ``hash`` is secure hash function. Thus, the following configuration
.. code-block::
"username","password","access"
"username","$6$rounds=656000$mWBiecMPrHAL1VgX$oU4Y5HH8HzlvMaxwkNEJjK13ozElyU1wAHBoO/WW5dAaE4YEfnB0X3FxbynKMl4FBdC3Ovap0jINz4LPkNADg0","read"
means that there is user ``username`` with ``read`` access and password ``password`` hashed by ``sha512`` with salt ``salt``.
OAuth provider uses library definitions (\ ``aioauth-client``\ ) in order *authenticate* users. It still requires user permission to be set in database, thus it inherits mapping provider without any changes. Whereas we could override ``check_credentials`` (authentication method) by something custom, OAuth flow is a bit more complex than just forward request, thus we have to implement the flow in login form.
OAuth's implementation also allows authenticating users via username + password (in the same way as mapping does) though it is not recommended for end-users and password must be left blank. In particular this feature is used by service reporting (aka robots).
In order to configure users there are special commands.
Remote synchronization
^^^^^^^^^^^^^^^^^^^^^^
There are several supported synchronization providers, currently they are ``rsync``\ , ``s3``\ , ``github``.
``rsync`` provider does not have any specific logic except for running external rsync application with configured arguments. The service does not handle SSH configuration, thus it has to be configured before running application manually.
``s3`` provider uses ``boto3`` package and implements sync feature. The files are stored in architecture directory (e.g. if bucket is ``repository``\ , packages will be stored in ``repository/x86_64`` for the ``x86_64`` architecture), bucket must be created before any action and API key must have permissions to write to the bucket. No external configuration required. In order to upload only changed files the service compares calculated hashes with the Amazon ETags, used realization is described `here <https://teppen.io/2018/10/23/aws_s3_verify_etags/>`_.
``github`` provider authenticates through basic auth, API key with repository write permissions is required. There will be created a release with the name of the architecture in case if it does not exist; files will be uploaded to the release assets. It also stores array of files and their MD5 checksums in release body in order to upload only changed ones. According to the Github API in case if there is already uploaded asset with the same name (e.g. database files), asset will be removed first.
Additional features
^^^^^^^^^^^^^^^^^^^
Some features require optional dependencies to be installed:
* Version control executables (e.g. ``git``\ , ``svn``\ ) for VCS packages.
* ``gnupg`` application for package and repository sign feature.
* ``rsync`` application for rsync based repository sync.
* ``boto3`` python package for ``S3`` sync.
* ``Jinja2`` python package for HTML report generation (it is also used by web application).
Web application
---------------
Web application requires the following python packages to be installed:
* Core part requires ``aiohttp`` (application itself), ``aiohttp_jinja2`` and ``Jinja2`` (HTML generation from templates).
* In addition, ``aiohttp_debugtoolbar`` is required for debug panel. Please note that this option does not work together with authorization and basically must not be used in production.
* In addition, authorization feature requires ``aiohttp_security``\ , ``aiohttp_session`` and ``cryptography``.
* In addition to base authorization dependencies, OAuth2 also requires ``aioauth-client`` library.
Middlewares
^^^^^^^^^^^
Service provides some custom middlewares, e.g. logging every exception (except for user ones) and user authorization.
Web views
^^^^^^^^^
All web views are defined in separated package and derived from ``ahriman.web.views.base.Base`` class which provides typed interfaces for web application.
REST API supports both form and JSON data, but the last one is recommended.
Different APIs are separated into different packages:
* ``ahriman.web.views.service`` provides views for application controls.
* ``ahriman.web.views.status`` package provides REST API for application reporting.
* ``ahriman.web.views.user`` package provides login and logout methods which can be called without authorization.
Templating
^^^^^^^^^^
Package provides base jinja templates which can be overridden by settings. Vanilla templates are actively using bootstrap library.
Requests and scopes
^^^^^^^^^^^^^^^^^^^
Service provides optional authorization which can be turned on in settings. In order to control user access there are two levels of authorization - read-only (only GET-like requests) and write (anything) which are provided by each web view directly.
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.
For authenticated users it uses encrypted session cookies to store tokens; encryption key is generated each time at the start of the application. It also stores expiration time of the session inside.
External calls
^^^^^^^^^^^^^^
Web application provides external calls to control main service. It spawns child process with specific arguments and waits for its termination. This feature must be used either with authorization or in safe (i.e. when status page is not available world-wide) environment.

View File

@ -1,10 +0,0 @@
Commands help
=============
ahriman
-------
.. argparse::
:module: ahriman.application.ahriman
:func: _parser
:prog: ahriman

View File

@ -1,108 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
from pathlib import Path
from unittest import mock
from ahriman.version import __version__
basedir = Path(__file__).resolve().parent.parent / "src"
sys.path.insert(0, str(basedir))
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
for module in (
"aioauth_client",
"aiohttp",
"aiohttp.web",
"aiohttp.web_exceptions",
"aiohttp.web_response",
"aiohttp.web_urldispatcher",
"aiohttp_jinja2",
"aiohttp_security",
"aiohttp_session",
"aiohttp_session.cookie_storage",
"boto3",
"cryptography",
"pyalpm",
):
sys.modules[module] = mock.Mock()
# -- Project information -----------------------------------------------------
project = "ahriman"
copyright = "2021-2022, ahriman team"
author = "ahriman team"
# The full version, including alpha/beta/rc tags
release = __version__
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinxarg.ext",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "default" if on_rtd else "alabaster"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
add_module_names = False
modindex_common_prefix = ["ahriman.application.", "ahriman.core.", "ahriman.models.", "ahriman.web."]
# -- Extension configuration -------------------------------------------------
autoclass_content = "both"
autodoc_member_order = "groupwise"
autodoc_default_options = {
"no-undoc-members": True,
"special-members": "__init__",
}

View File

@ -1,206 +0,0 @@
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.
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:
* By default, it splits value by spaces excluding empty elements.
* In case if quotation mark (\ ``"`` or ``'``\ ) will be found, any spaces inside will be ignored.
* In order to use quotation mark inside value it is required to put it to another quotation mark, e.g. ``wor"'"d "with quote"`` will be parsed as ``["wor'd", "with quote"]`` and vice versa.
* Unclosed quotation mark is not allowed and will rise an exception.
Path values, except for casting to ``pathlib.Path`` type, will be also expanded to absolute paths relative to the configuration path. E.g. if path is set to ``ahriman.ini.d/logging.ini`` and root configuration path is ``/etc/ahriman.ini``\ , the value will be expanded to ``/etc/ahriman.ini.d/logging.ini``. In order to disable path expand, use the full path, e.g. ``/etc/ahriman.ini.d/logging.ini``.
``settings`` group
------------------
Base configuration settings.
* ``include`` - path to directory with configuration files overrides, string, required.
* ``database`` - path to SQLite database, string, required.
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
``alpm`` group
--------------
libalpm and AUR related configuration.
* ``database`` - path to pacman local database cache, string, required.
* ``repositories`` - list of pacman repositories, space separated list of strings, required.
* ``root`` - root for alpm library, string, required.
``auth`` group
--------------
Base authorization settings. ``OAuth`` provider requires ``aioauth-client`` library to be installed.
* ``target`` - specifies authorization provider, string, optional, default ``disabled``. Allowed values are ``disabled``\ , ``configuration``\ , ``oauth``.
* ``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.
``build:*`` groups
------------------
Build related configuration. Group name can refer to architecture, e.g. ``build:x86_64`` can be used for x86_64 architecture specific settings.
* ``archbuild_flags`` - additional flags passed to ``archbuild`` command, space separated list of strings, optional.
* ``build_command`` - default build command, string, required.
* ``ignore_packages`` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional.
* ``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.
``repository`` group
--------------------
Base repository settings.
* ``name`` - repository name, string, required.
* ``root`` - root path for application, string, required.
``sign:*`` groups
-----------------
Settings for signing packages or repository. Group name can refer to architecture, e.g. ``sign:x86_64`` can be used for x86_64 architecture specific settings.
* ``target`` - configuration flag to enable signing, space separated list of strings, required. Allowed values are ``package`` (sign each package separately), ``repository`` (sign repository database file).
* ``key`` - default PGP key, string, required. This key will also be used for database signing if enabled.
* ``key_*`` settings - PGP key which will be used for specific packages, string, optional. For example, if there is ``key_yay`` option the specified key will be used for yay package and default key for others.
``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).
Type will be read from several ways:
* In case if ``type`` option set inside the section, it will be used.
* Otherwise, it will look for type from section name removing architecture name.
* And finally, it will use section name as type.
``console`` type
^^^^^^^^^^^^^^^^
Section name must be either ``console`` (plus optional architecture name, e.g. ``console:x86_64``\ ) or random name with ``type`` set.
* ``use_utf`` - use utf8 symbols in output if set and ascii otherwise, boolean, optional, default ``yes``.
``email`` type
^^^^^^^^^^^^^^
Section name must be either ``email`` (plus optional architecture name, e.g. ``email:x86_64``\ ) or random name with ``type`` set.
* ``type`` - type of the report, string, optional, must be set to ``email`` if exists.
* ``full_template_path`` - path to Jinja2 template for full package description index, string, optional.
* ``homepage`` - link to homepage, string, optional.
* ``host`` - SMTP host for sending emails, string, required.
* ``link_path`` - prefix for HTML links, string, required.
* ``no_empty_report`` - skip report generation for empty packages list, boolean, optional, default ``yes``.
* ``password`` - SMTP password to authenticate, string, optional.
* ``port`` - SMTP port for sending emails, int, required.
* ``receivers`` - SMTP receiver addresses, space separated list of strings, required.
* ``sender`` - SMTP sender address, string, required.
* ``ssl`` - SSL mode for SMTP connection, one of ``ssl``\ , ``starttls``\ , ``disabled``\ , optional, default ``disabled``.
* ``template_path`` - path to Jinja2 template, string, required.
* ``user`` - SMTP user to authenticate, string, optional.
``html`` type
^^^^^^^^^^^^^
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.
* ``template_path`` - path to Jinja2 template, string, required.
``telegram`` type
^^^^^^^^^^^^^^^^^
Section name must be either ``telegram`` (plus optional architecture name, e.g. ``telegram:x86_64``\ ) or random name with ``type`` set.
* ``type`` - type of the report, string, optional, must be set to ``telegram`` if exists.
* ``api_key`` - telegram bot API key, string, required. Please refer FAQ about how to create chat and bot
* ``chat_id`` - telegram chat id, either string with ``@`` or integer value, required.
* ``homepage`` - link to homepage, string, optional.
* ``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``.
``upload`` group
----------------
Remote synchronization settings.
* ``target`` - list of synchronizations to be used, space separated list of strings, required. It must point to valid section (or to section with architecture), e.g. ``somerandomname`` must point to existing section, ``github`` must point to one of ``github`` of ``github:x86_64`` (with architecture it has higher priority).
Type will be read from several ways:
* In case if ``type`` option set inside the section, it will be used.
* Otherwise, it will look for type from section name removing architecture name.
* And finally, it will use section name as type.
``github`` type
^^^^^^^^^^^^^^^
This feature requires Github key creation (see below). Section name must be either ``github`` (plus optional architecture name, e.g. ``github:x86_64``\ ) or random name with ``type`` set.
* ``type`` - type of the upload, string, optional, must be set to ``github`` if exists.
* ``owner`` - Github repository owner, string, required.
* ``password`` - created Github API key. In order to create it do the following:
#. Go to `settings page <https://github.com/settings/profile>`_.
#. Switch to `developers settings <https://github.com/settings/apps>`_.
#. Switch to `personal access tokens <https://github.com/settings/tokens>`_.
#. 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).
* ``username`` - Github authorization user, string, required. Basically the same as ``owner``.
``rsync`` type
^^^^^^^^^^^^^^
Requires ``rsync`` package to be installed. Do not forget to configure ssh for user ``ahriman``. Section name must be either ``rsync`` (plus optional architecture name, e.g. ``rsync:x86_64``\ ) or random name with ``type`` set.
* ``type`` - type of the upload, string, optional, must be set to ``rsync`` if exists.
* ``command`` - rsync command to run, space separated list of string, required.
* ``remote`` - remote server to rsync (e.g. ``1.2.3.4:path/to/sync``\ ), string, required.
``s3`` type
^^^^^^^^^^^
Requires ``boto3`` library to be installed. Section name must be either ``s3`` (plus optional architecture name, e.g. ``s3:x86_64``\ ) or random name with ``type`` set.
* ``type`` - type of the upload, string, optional, must be set to ``github`` if exists.
* ``access_key`` - AWS access key ID, string, required.
* ``bucket`` - bucket name (e.g. ``bucket``\ ), string, required.
* ``chunk_size`` - chunk size for calculating entity tags, int, optional, default 8 * 1024 * 1024.
* ``region`` - bucket region (e.g. ``eu-central-1``\ ), string, required.
* ``secret_key`` - AWS secret access key, string, required.
``web:*`` groups
----------------
Web server settings. If any of ``host``\ /\ ``port`` is not set, web integration will be disabled. Group name can refer to architecture, e.g. ``web:x86_64`` can be used for x86_64 architecture specific settings. This feature requires ``aiohttp`` libraries to be installed.
* ``address`` - optional address in form ``proto://host:port`` (\ ``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used.
* ``debug`` - enable debug toolbar, boolean, optional, default ``no``.
* ``debug_check_host`` - check hosts to access debug toolbar, boolean, optional, default ``no``.
* ``debug_allowed_hosts`` - allowed hosts to get access to debug toolbar, space separated list of string, optional.
* ``host`` - host to bind, string, optional.
* ``index_url`` - full url of the repository index page, string, optional.
* ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.
* ``port`` - port to bind, int, optional.
* ``static_path`` - path to directory with static files, string, required.
* ``templates`` - path to templates directory, string, required.
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.

View File

@ -1,703 +0,0 @@
FAQ
===
General topics
--------------
What is the purpose of the project?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This project has been created in order to maintain self-hosted Arch Linux user repository without manual intervention - checking for updates and building packages.
How do I install it?
^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
yay -S ahriman
sudo ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "repository"
systemctl enable --now ahriman@x86_64.timer
Long answer
"""""""""""
The idea is to install the package as usual, create working directory tree, create configuration for ``sudo`` and ``devtools``. Detailed description of the setup instruction can be found :doc:`here <setup>`.
What does "architecture specific" mean? / How to configure for different architectures?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some sections can be configured per architecture. The service will merge architecture specific values into common settings. In order to specify settings for specific architecture you must point it in section name.
For example, the section
.. code-block:: ini
[build]
build_command = extra-x86_64-build
states that default build command is ``extra-x86_64-build``. But if there is section
.. code-block:: ini
[build:i686]
build_command = extra-i686-build
the ``extra-i686-build`` command will be used for ``i686`` architecture.
How to use reporter/upload settings?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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:
.. code-block:: ini
[report]
target = email
[email]
...
or in case of multiple architectures and *different* reporting settings:
.. code-block:: ini
[report]
target = email
[email:i686]
...
[email:x86_64]
...
But for some cases you would like to have multiple different reports with the same type (e.g. sending different templates to different addresses). For these cases you will need to specify section name in target and type in section, e.g. the following configuration can be used:
.. code-block:: ini
[report]
target = email_1 email_2
[email_1]
type = email
...
[email_2]
type = email
...
Okay, I've installed ahriman, how do I add new package?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
sudo -u ahriman ahriman package-add ahriman --now
``--now`` flag is totally optional and just run ``repo-update`` subcommand after the registering the new package, Thus the extended flow is the following:
.. code-block:: shell
sudo -u ahriman ahriman package-add ahriman
sudo -u ahriman ahriman repo-update
AUR is fine, but I would like to create package from local PKGBUILD
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
sudo -u ahriman ahriman package-add /path/to/local/directory/with/PKGBUILD --now
Before using this command you will need to create local directory, put ``PKGBUILD`` there and generate ``.SRCINFO`` by using ``makepkg --printsrcinfo > .SRCINFO`` command. These packages will be stored locally and *will be ignored* during automatic update; in order to update the package you will need to run ``package-add`` command again.
But I just wanted to change PKGBUILD from AUR a bit!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Well it is supported also.
#. 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``.
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
Package build fails because it cannot validate PGP signature of source files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
sudo -u ahriman ahriman key-import ...
How do I check if there are new commits for VCS packages?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally the service handles VCS packages correctly, but 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
sudo -u ahriman ahriman package-remove ahriman
Also, there is command ``repo-remove-unknown`` which checks packages in AUR and local storage and removes ones which have been removed.
Remove commands also remove any package files (patches, caches etc).
There is new major release of %library-name%, how do I rebuild packages?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TL;DR
.. code-block:: shell
sudo -u ahriman ahriman repo-rebuild --depends-on python
You can even rebuild the whole repository (which is particular useful in case if you would like to change packager) if you do not supply ``--depends-on`` option.
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?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to your ``pacman.conf``\ :
.. code-block:: ini
[repository]
Server = file:///var/lib/ahriman/repository/x86_64
(You might need to add ``SigLevel`` option according to the pacman documentation.)
I would like to serve the repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Easy. For example, nginx configuration (without SSL) will look like:
.. code-block::
server {
listen 80;
server_name repo.example.com;
location / {
autoindex on;
root /var/lib/ahriman/repository;
}
}
Example of the status page configuration is the following (status service is using 8080 port):
.. code-block::
server {
listen 80;
server_name builds.example.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
proxy_pass http://127.0.0.1:8080;
}
}
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.
The default action (in case if no arguments provided) is ``repo-update``. Basically the idea is to run container, e.g.:
.. code-block:: shell
docker run --privileged -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
``--privileged`` flag is required to make mount possible inside container. In addition, you can pass own configuration overrides by using the same ``-v`` flag, e.g.:
.. code-block:: shell
docker run -v /path/to/local/repo:/var/lib/ahriman -v /etc/ahriman.ini:/etc/ahriman.ini.d/10-overrides.ini arcan1s/ahriman:latest
By default, it runs ``repo-update``\ , but it can be overwritten to any other command you would like to, e.g.:
.. code-block:: shell
docker run arcan1s/ahriman:latest package-add ahriman --now
For more details please refer to docker FAQ.
Environment variables
^^^^^^^^^^^^^^^^^^^^^
The following environment variables are supported:
* ``AHRIMAN_ARCHITECTURE`` - architecture of the repository, default is ``x86_64``.
* ``AHRIMAN_DEBUG`` - if set all commands will be logged to console.
* ``AHRIMAN_FORCE_ROOT`` - force run ahriman as root instead of guessing by subcommand.
* ``AHRIMAN_HOST`` - host for the web interface, default is ``0.0.0.0``.
* ``AHRIMAN_OUTPUT`` - controls logging handler, e.g. ``syslog``\ , ``console``. The name must be found in logging configuration. Note that if ``syslog`` (the default) handler is used you will need to mount ``/dev/log`` inside container because it is not available there.
* ``AHRIMAN_PACKAGER`` - packager name from which packages will be built, default is ``ahriman bot <ahriman@example.com>``.
* ``AHRIMAN_PORT`` - HTTP server port if any, default is empty.
* ``AHRIMAN_REPOSITORY`` - repository name, default is ``aur-clone``.
* ``AHRIMAN_REPOSITORY_ROOT`` - repository root. Because of filesystem rights it is required to override default repository root. By default, it uses ``ahriman`` directory inside ahriman's home, which can be passed as mount volume.
* ``AHRIMAN_USER`` - ahriman user, usually must not be overwritten, default is ``ahriman``.
You can pass any of these variables by using ``-e`` argument, e.g.:
.. code-block:: shell
docker run -e AHRIMAN_PORT=8080 arcan1s/ahriman:latest
Web service setup
^^^^^^^^^^^^^^^^^
Well for that you would need to have web container instance running forever; it can be achieved by the following command:
.. code-block:: shell
docker run -p 8080:8080 -e AHRIMAN_PORT=8080 -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Note about ``AHRIMAN_PORT`` environment variable which is required in order to enable web service. An additional port bind by ``-p 8080:8080`` is required to pass docker port outside of container.
For every next container run use arguments ``-e AHRIMAN_PORT=8080 --net=host``\ , e.g.:
.. code-block:: shell
docker run --privileged -e AHRIMAN_PORT=8080 --net=host -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
Remote synchronization
----------------------
Wait I would like to use the repository from another server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are several choices:
#.
Easy and cheap, just share your local files through the internet, e.g. for ``nginx``\ :
.. code-block::
server {
location /x86_64 {
root /var/lib/ahriman/repository/x86_64;
autoindex on;
}
}
#.
You can also upload your packages using ``rsync`` to any available server. In order to use it you would need to configure ahriman first:
.. code-block:: ini
[upload]
target = rsync
[rsync]
remote = 192.168.0.1:/srv/repo
After that just add ``/srv/repo`` to the ``pacman.conf`` as usual. You can also upload to S3 (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?
^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
pacman -S python-boto3
#.
Create a bucket.
#.
Create user with write access to the bucket:
.. code-block::
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListObjectsInBucket",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::repository"
]
},
{
"Sid": "AllObjectActions",
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": [
"arn:aws:s3:::repository/*"
]
}
]
}
#.
Create an API key for the user and store it.
#.
Configure the service as following:
.. code-block:: ini
[upload]
target = s3
[s3]
access_key = ...
bucket = repository
region = eu-central-1
secret_key = ...
How do I configure Github?
^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create a repository.
#.
`Create API key <https://github.com/settings/tokens>`_ with scope ``public_repo``.
#.
Configure the service as following:
.. code-block:: ini
[upload]
target = github
[github]
owner = ahriman
password = ...
repository = repository
username = ahriman
Reporting
---------
I would like to get report to email
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
yay -S python-jinja
#.
Configure the service:
.. code-block:: ini
[report]
target = email
[email]
host = smtp.example.com
link_path = http://example.com/x86_64
password = ...
port = 465
receivers = me@example.com
sender = me@example.com
user = me@example.com
I'm using synchronization to S3 and would like to generate index page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
yay -S python-jinja
#.
Configure the service:
.. code-block:: ini
[report]
target = html
[html]
path = /var/lib/ahriman/repository/x86_64/index.html
link_path = http://example.com/x86_64
After these steps ``index.html`` file will be automatically synced to S3
I would like to get messages to my telegram account/channel
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
It still requires additional dependencies:
.. code-block:: shell
yay -S python-jinja
#.
Register bot in telegram. You can do it by talking with `@BotFather <https://t.me/botfather>`_. For more details please refer to `official documentation <https://core.telegram.org/bots>`_.
#.
Optionally (if you want to post message in chat):
#. Create telegram channel.
#. Invite your bot into the channel.
#. Make your channel public
#.
Get chat id if you want to use by numerical id or just use id prefixed with ``@`` (e.g. ``@ahriman``\ ). If you are not using chat the chat id is your user id. If you don't want to make channel public you can use `this guide <https://stackoverflow.com/a/33862907>`_.
#.
Configure the service:
.. code-block:: ini
[report]
target = telegram
[telegram]
api_key = aaAAbbBBccCC
chat_id = @ahriman
link_path = http://example.com/x86_64
``api_key`` is the one sent by `@BotFather <https://t.me/botfather>`_\ , ``chat_id`` is the value retrieved from previous step.
If you did everything fine you should receive the message with the next update. Quick credentials check can be done by using the following command:
.. code-block:: shell
curl 'https://api.telegram.org/bot${CHAT_ID}/sendMessage?chat_id=${API_KEY}&text=hello'
(replace ``${CHAT_ID}`` and ``${API_KEY}`` with the values from configuration).
Web service
-----------
Readme mentions web interface, how do I use it?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies:
.. code-block:: shell
yay -S python-aiohttp python-aiohttp-jinja2
#.
Configure service:
.. code-block:: ini
[web]
port = 8080
#.
Start the web service ``systemctl enable --now ahriman-web@x86_64``.
I would like to limit user access to the status page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install dependencies 😊:
.. code-block:: shell
yay -S python-aiohttp-security python-aiohttp-session python-cryptography
#.
Configure the service to enable authorization:
.. code-block:: ini
[auth]
target = configuration
#.
Create user for the service:
.. code-block:: shell
sudo -u ahriman ahriman user-add --as-service -r write api
This command will ask for the password, just type it in stdin; *do not* leave the field blank, user will not be able to authorize.
#.
Create end-user ``sudo -u ahriman ahriman user-add -r write my-first-user`` with password.
#. Restart web service ``systemctl restart ahriman-web@x86_64``.
I would like to use OAuth
^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create OAuth web application, download its ``client_id`` and ``client_secret``.
#.
Guess what? Install dependencies:
.. code-block:: shell
yay -S python-aiohttp-security python-aiohttp-session python-cryptography python-aioauth-client
#.
Configure the service:
.. code-block:: ini
[auth]
target = oauth
client_id = ...
client_secret = ...
[web]
address = https://example.com
Configure ``oauth_provider`` and ``oauth_scopes`` in case if you would like to use different from Google provider. Scope must grant access to user email. ``web.address`` is required to make callback URL available from internet.
#.
Create service user:
.. code-block:: shell
sudo -u ahriman ahriman user-add --as-service -r write api
#.
Create end-user ``sudo -u ahriman ahriman user-add -r write my-first-user``. When it will ask for the password leave it blank.
#. Restart web service ``systemctl restart ahriman-web@x86_64``.
Backup and restore
------------------
The service provides several commands aim to do easy repository backup and restore. If you would like to move repository from the server ``server1.example.com`` to another ``server2.example.com`` you have to perform the following steps:
#.
On the source server ``server1.example.com`` run ``repo-backup`` command, e.g.:
.. code-block:: shell
sudo ahriman repo-backup /tmp/repo.tar.gz
This command will pack all configuration files together with database file into the archive specified as command line argument (i.e. ``/tmp/repo.tar.gz``\ ). In addition it will also archive ``cache`` directory (the one which contains local clones used by e.g. local packages) and ``.gnupg`` of the ``ahriman`` user.
#.
Copy created archive from source server ``server1.example.com`` to target ``server2.example.com``.
#.
Install ahriman as usual on the target server ``server2.example.com`` if you didn't yet.
#.
Extract archive e.g. by using subcommand:
.. code-block:: shell
sudo ahriman repo-restore /tmp/repo.tar.gz
An additional argument ``-o``\ /\ ``--output`` can be used to specify extraction root (\ ``/`` by default).
#.
Rebuild repository:
.. code-block:: shell
sudo -u ahriman ahriman repo-rebuild --from-database
Other topics
------------
How does it differ from %another-manager%?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Short answer - I do not know.
`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.
* ``archrepo2`` actively uses direct shell calls and ``yaourt`` components.
* It 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).
* ``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``.
`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.
``repo-scripts`` also have 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, the service writes logs to ``/dev/log`` which can be accessed by using ``journalctl`` command (logs are written to the journal of the user under which command is run).
You can also edit configuration and forward logs to ``stderr``\ , just change ``handlers`` value, e.g.:
.. code-block:: shell
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>`_.
Html customization
^^^^^^^^^^^^^^^^^^
It is possible to customize html templates. In order to do so, create files somewhere (refer to Jinja2 documentation and the service source code for available parameters) and put ``template_path`` to configuration pointing to this directory.
I did not find my question
^^^^^^^^^^^^^^^^^^^^^^^^^^
`Create an issue <https://github.com/arcan1s/ahriman/issues>`_ with type **Question**.

View File

@ -1,37 +0,0 @@
Welcome to ahriman's documentation!
===================================
Wrapper for managing custom repository inspired by `repo-scripts <https://github.com/arcan1s/repo-scripts>`_.
Features
--------
* Install-configure-forget manager for 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).
* Dependency manager.
* Ability to patch AUR packages and even create package from local PKGBUILDs.
* Repository status interface with optional authorization and control options.
Contents
--------
.. toctree::
:maxdepth: 2
setup
configuration
command-line
faq
architecture
advanced-usage
modules
Indices and tables
------------------
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,7 +0,0 @@
Modules
=======
.. toctree::
:maxdepth: 4
ahriman

View File

@ -1,76 +0,0 @@
Initial setup
=============
#.
Install package as usual.
#.
Change settings if required, see :doc:`configuration reference <configuration>` for more details.
#.
TL;DR
.. code-block:: shell
sudo ahriman -a x86_64 repo-setup ...
``repo-setup`` literally does the following steps:
#.
Create ``/var/lib/ahriman/.makepkg.conf`` with ``makepkg.conf`` overrides if required (at least you might want to set ``PACKAGER``\ ):
.. code-block:: shell
echo 'PACKAGER="John Doe <john@doe.com>"' | sudo -u ahriman tee -a /var/lib/ahriman/.makepkg.conf
#.
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.
.. code-block:: shell
ln -s /usr/bin/archbuild /usr/local/bin/ahriman-x86_64-build
cp /usr/share/devtools/pacman-{extra,ahriman}.conf
echo '[multilib]' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'Include = /etc/pacman.d/mirrorlist' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo '[aur-clone]' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'SigLevel = Optional TrustAll' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo 'Server = file:///var/lib/ahriman/repository/$arch' | tee -a /usr/share/devtools/pacman-ahriman.conf
echo '[build]' | tee -a /etc/ahriman.ini.d/build.ini
echo 'build_command = ahriman-x86_64-build' | tee -a /etc/ahriman.ini.d/build.ini
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
#.
Start and enable ``ahriman@.timer`` via ``systemctl``\ :
.. code-block:: shell
systemctl enable --now ahriman@x86_64.timer
#.
Start and enable status page:
.. code-block:: shell
systemctl enable --now ahriman-web@x86_64
#.
Add packages by using ``ahriman package-add {package}`` command:
.. code-block:: shell
sudo -u ahriman ahriman -a x86_64 package-add ahriman --now

35
make_release.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
set -e
VERSION="$1"
ARCHIVE="ahriman"
FILES="COPYING CONFIGURING.md README.md package src setup.py"
IGNORELIST="build .idea package/archlinux package/*src.tar.xz"
# set version
sed -i "/__version__ = '[0-9.]*/s/[^'][^)]*/__version__ = '$VERSION'/" src/ahriman/version.py
# create archive
[[ -e $ARCHIVE-$VERSION-src.tar.xz ]] && rm -f "$ARCHIVE-$VERSION-src.tar.xz"
[[ -d $ARCHIVE ]] && rm -rf "$ARCHIVE"
mkdir "$ARCHIVE"
for FILE in ${FILES[*]}; do cp -r "$FILE" "$ARCHIVE"; done
for FILE in ${IGNORELIST[*]}; do rm -rf "$ARCHIVE/$FILE"; done
tar cJf "$ARCHIVE-$VERSION-src.tar.xz" "$ARCHIVE"
rm -rf "$ARCHIVE"
# update checksums
SHA512SUMS=$(sha512sum $ARCHIVE-$VERSION-src.tar.xz | awk '{print $1}')
sed -i "/sha512sums=('[0-9A-Fa-f]*/s/[^'][^)]*/sha512sums=('$SHA512SUMS'/" package/archlinux/PKGBUILD
sed -i "s/pkgver=[0-9.]*/pkgver=$VERSION/" package/archlinux/PKGBUILD
# clear
find . -type f -name '*src.tar.xz' -not -name "*$VERSION-src.tar.xz" -exec rm -f {} \;
read -p "Publish release? [Ny] " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
git add package/archlinux/PKGBUILD
git commit -m "Release $VERSION" && git push
git tag "$VERSION" && git push --tags
fi

View File

@ -1,54 +1,45 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=2.0.0rc9
pkgver=0.9.0
pkgrel=1
pkgdesc="ArcH Linux ReposItory MANager"
pkgdesc="ArcHlinux ReposItory MANager"
arch=('any')
url="https://github.com/arcan1s/ahriman"
license=('GPL3')
depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
makedepends=('python-build' 'python-installer' 'python-wheel')
optdepends=('breezy: -bzr packages support'
depends=('devtools' 'expac' 'git' 'python-aur' 'python-srcinfo')
makedepends=('python-pip')
optdepends=('aws-cli: sync to s3'
'breezy: -bzr packages support'
'darcs: -darcs packages support'
'gnupg: package and repository sign'
'mercurial: -hg packages support'
'python-aioauth-client: web server with OAuth2 authorization'
'python-aiohttp: web server'
'python-aiohttp-debugtoolbar: web server with enabled debug panel'
'python-aiohttp-jinja2: web server'
'python-aiohttp-security: web server with authorization'
'python-aiohttp-session: web server with authorization'
'python-boto3: sync to s3'
'python-cryptography: web server with authorization'
'python-jinja: html report generation'
'rsync: sync by using rsync'
'subversion: -svn packages support')
source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgname-$pkgver-src.tar.xz"
'ahriman.sudoers'
'ahriman.sysusers'
'ahriman.tmpfiles')
sha512sums=('a44af5545204484452ffab2ad1819ad19a4c09f164fd1787dea9cc96474e559f898a6c58f25d70e8e4f505e55ee07b3d3db477a2f412f46e52c252c72bc05f55'
'8c9b5b63ac3f7b4d9debaf801a1e9c060877c33d3ecafe18010fcca778e5fa2f2e46909d3d0ff1b229ff8aa978445d8243fd36e1fc104117ed678d5e21901167'
'13718afec2c6786a18f0b223ef8e58dccf0688bca4cdbe203f14071f5031ed20120eb0ce38b52c76cfd6e8b6581a9c9eaa2743eb11abbaca637451a84c33f075'
'55b20f6da3d66e7bbf2add5d95a3b60632df121717d25a993e56e737d14f51fe063eb6f1b38bd81cc32e05db01c0c1d80aaa720c45cde87f238d8b46cdb8cbc4')
backup=('etc/ahriman.ini'
'etc/ahriman.ini.d/logging.ini')
build() {
cd "$pkgname"
python -m build --wheel --no-isolation
python setup.py build
}
package() {
cd "$pkgname"
python -m installer --destdir="$pkgdir" "dist/$pkgname-$pkgver-py3-none-any.whl"
# python-installer actually thinks that you cannot just copy files to root
# thus we need to copy them manually
install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini"
install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini.d/logging.ini" "$pkgdir/etc/ahriman.ini.d/logging.ini"
python setup.py install --root="$pkgdir"
install -Dm400 "$srcdir/$pkgname.sudoers" "$pkgdir/etc/sudoers.d/$pkgname"
install -Dm644 "$srcdir/$pkgname.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgname.conf"
install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf"
}
sha512sums=('112b0d8aac68e5330bbdd2b86a59c8a9af8ab7a7c636489623c8460bb90f1318585851edd2a97a8ce20e2d2ad93b847b522685df707c190aa39d23ab908fa8ef'
'53d37efec812afebf86281716259f9ea78a307b83897166c72777251c3eebcb587ecee375d907514781fb2a5c808cbb24ef9f3f244f12740155d0603bf213131'
'62b2eccc352d33853ef243c9cddd63663014aa97b87242f1b5bc5099a7dbd69ff3821f24ffc58e1b7f2387bd4e9e9712cc4c67f661b1724ad99cdf09b3717794')

View File

@ -0,0 +1,4 @@
# Used by ArcHlinux ReposItory MANager with default settings
Cmnd_Alias ARCHBUILD_CMD = /usr/bin/extra-x86_64-build *, /usr/bin/multilib-build *
ahriman ALL=(ALL) NOPASSWD: ARCHBUILD_CMD

View File

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

View File

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

39
package/etc/ahriman.ini Normal file
View File

@ -0,0 +1,39 @@
[settings]
include = /etc/ahriman.ini.d
logging = /etc/ahriman.ini.d/logging.ini
[aur]
url = https://aur.archlinux.org
[build_x86_64]
archbuild_flags =
build_command = extra-x86_64-build
ignore_packages =
makechrootpkg_flags =
makepkg_flags = --skippgpcheck
[repository]
name = aur-clone
root = /var/lib/ahriman
[sign]
enabled = disabled
key =
[report]
target =
[html_x86_64]
path =
homepage =
link_path =
template_path = /usr/share/ahriman/index.jinja2
[upload]
target =
[s3_x86_64]
bucket =
[rsync_x86_64]
remote =

View File

@ -0,0 +1,47 @@
[loggers]
keys = root,builder,build_details
[handlers]
keys = console_handler,build_file_handler,file_handler
[formatters]
keys = generic_format
[handler_console_handler]
class = StreamHandler
level = DEBUG
formatter = generic_format
args = (sys.stdout,)
[handler_file_handler]
class = logging.handlers.RotatingFileHandler
level = DEBUG
formatter = generic_format
args = ('/var/log/ahriman/ahriman.log', 'a', 20971520, 20)
[handler_build_file_handler]
class = logging.handlers.RotatingFileHandler
level = DEBUG
formatter = generic_format
args = ('/var/log/ahriman/build.log', 'a', 20971520, 20)
[formatter_generic_format]
format = %(asctime)s : %(levelname)s : %(funcName)s : %(message)s
datefmt =
[logger_root]
level = DEBUG
handlers = file_handler
qualname = root
[logger_builder]
level = DEBUG
handlers = file_handler
qualname = builder
propagate = 0
[logger_build_details]
level = DEBUG
handlers = build_file_handler
qualname = build_details
propagate = 0

View File

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

View File

@ -0,0 +1,7 @@
[Unit]
Description=ArcHlinux ReposItory MANager
[Service]
ExecStart=/usr/bin/ahriman --architecture x86_64 update
User=ahriman
Group=ahriman

View File

@ -1,5 +1,5 @@
[Unit]
Description=ArcH Linux ReposItory MANager timer (%I architecture)
Description=ArcHlinux ReposItory MANager timer
[Timer]
OnCalendar=daily

View File

@ -1,7 +0,0 @@
[Unit]
Description=ArcH Linux ReposItory MANager (%I architecture)
[Service]
ExecStart=/usr/bin/ahriman --architecture %i update
User=ahriman
Group=ahriman

View File

@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<title>{{ repository|e }}</title>
</head>
<body>
<h1>{{ repository|e }} ArchLinux custom repository</h1>
{% if pgp_key is not none %}
<p>All packages are signed with <a href="http://keys.gnupg.net/pks/lookup?search=0x{{ pgp_key|e }}" title="key search">{{ pgp_key|e }}</a>.</p>
{% endif %}
<code>
$ cat /etc/pacman.conf<br>
[{{ repository|e }}]<br>
Server = {{ link_path|e }}
</code>
<p>Packages:</p>
<ul>
{% for package, package_url in packages.items() %}
<li><a href="{{ package_url|e }}" title="{{ package|e }}">{{ package|e }}</a></li>
{% endfor %}
</ul>
{% if homepage is not none %}
<footer><a href="{{ homepage|e }}" title="homepage">Homepage</a></footer>
{% endif %}
</body>
</html>

View File

@ -1,65 +0,0 @@
[settings]
include = ahriman.ini.d
logging = ahriman.ini.d/logging.ini
database = /var/lib/ahriman/ahriman.db
[alpm]
database = /var/lib/pacman
repositories = core extra community multilib
root = /
[auth]
target = disabled
max_age = 604800
oauth_provider = GoogleClient
oauth_scopes = https://www.googleapis.com/auth/userinfo.email
safe_build_status = yes
[build]
archbuild_flags =
build_command = extra-x86_64-build
ignore_packages =
makechrootpkg_flags =
makepkg_flags = --nocolor
[repository]
name = aur-clone
root = /var/lib/ahriman
[sign]
target =
[report]
target = console
[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
[html]
template_path = /usr/share/ahriman/templates/repo-index.jinja2
[telegram]
template_path = /usr/share/ahriman/templates/telegram-index.jinja2
[upload]
target =
[rsync]
command = rsync --archive --compress --partial --delete
[s3]
chunk_size = 8388608
[web]
debug = no
debug_check_host = no
debug_allowed_hosts =
host = 127.0.0.1
static_path = /usr/share/ahriman/templates/static
templates = /usr/share/ahriman/templates

View File

@ -1,80 +0,0 @@
[loggers]
keys = root,build_details,database,http,stderr,boto3,botocore,nose,s3transfer
[handlers]
keys = console_handler,syslog_handler
[formatters]
keys = generic_format,syslog_format
[handler_console_handler]
class = StreamHandler
level = DEBUG
formatter = generic_format
args = (sys.stderr,)
[handler_syslog_handler]
class = logging.handlers.SysLogHandler
level = DEBUG
formatter = syslog_format
args = ("/dev/log",)
[formatter_generic_format]
format = [%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s
datefmt =
[formatter_syslog_format]
format = [%(levelname)s] [%(name)s] [%(filename)s:%(lineno)d] [%(funcName)s]: %(message)s
datefmt =
[logger_root]
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
qualname = http
propagate = 0
[logger_stderr]
level = DEBUG
handlers = console_handler
qualname = stderr
[logger_boto3]
level = INFO
handlers = syslog_handler
qualname = boto3
propagate = 0
[logger_botocore]
level = INFO
handlers = syslog_handler
qualname = botocore
propagate = 0
[logger_nose]
level = INFO
handlers = syslog_handler
qualname = nose
propagate = 0
[logger_s3transfer]
level = INFO
handlers = syslog_handler
qualname = s3transfer
propagate = 0

View File

@ -1,133 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>{{ repository }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/static/favicon.ico">
{% include "utils/style.jinja2" %}
</head>
<body>
<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 %}
</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>
<button id="update" class="btn btn-secondary" onclick="updatePackages()" disabled>
<i class="fa fa-play"></i> update
</button>
<button id="remove" class="btn btn-danger" onclick="removePackages()" disabled>
<i class="fa fa-trash"></i> remove
</button>
{% endif %}
</div>
<table id="packages" class="table table-striped table-hover"
data-click-to-select="true"
data-export-options='{"fileName": "packages"}'
data-page-list="[10, 25, 50, 100, all]"
data-page-size="10"
data-pagination="true"
data-resizable="true"
data-search="true"
data-show-columns="true"
data-show-columns-search="true"
data-show-columns-toggle-all="true"
data-show-export="true"
data-show-fullscreen="true"
data-show-search-clear-button="true"
data-sortable="true"
data-sort-reset="true"
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>
</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>
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
<ul class="nav">
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
</ul>
{% if index_url is not none %}
<ul class="nav">
<li><a class="nav-link" href="{{ index_url }}" title="repo index">repo index</a></li>
</ul>
{% endif %}
{% if auth.enabled %}
{% if auth.username is none %}
{{ auth.control|safe }}
{% else %}
<form action="/user-api/v1/logout" method="post">
<button class="btn btn-link" style="text-decoration: none">logout ({{ auth.username }})</button>
</form>
{% endif %}
{% endif %}
</footer>
</div>
{% if auth.enabled %}
{% 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" %}
</body>
</html>

View File

@ -1,29 +0,0 @@
<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">
<div class="modal-header">
<h4 class="modal-title">login</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">username</label>
<div class="col-sm-10">
<input id="username" type="text" class="form-control" placeholder="enter username" name="username" required>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">password</label>
<div class="col-sm-10">
<input id="password" type="password" class="form-control" placeholder="enter password" name="password" required>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary">login</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -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>

View File

@ -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>

View File

@ -1,42 +0,0 @@
{#simplified version of full report#}
<!doctype html>
<html lang="en">
<head>
<title>{{ repository }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
{% include "utils/style.jinja2" %}
</head>
<body>
<div class="container">
<table id="packages" class="table table-striped">
<thead class="table-primary">
<tr>
<th>package</th>
<th>version</th>
<th>archive size</th>
<th>installed size</th>
<th>build date</th>
</tr>
</thead>
<tbody>
{% for package in packages %}
<tr>
<td><a href="{{ link_path }}/{{ package.filename }}" title="{{ package.name }}">{{ package.name }}</a></td>
<td>{{ package.version }}</td>
<td>{{ package.archive_size }}</td>
<td>{{ package.installed_size }}</td>
<td>{{ package.build_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>

View File

@ -1,98 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>{{ repository }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
{% include "utils/style.jinja2" %}
</head>
<body>
<div class="container">
<h1>Arch Linux user repository</h1>
</div>
<div class="container">
{% if pgp_key is not none %}
<p>This repository is signed with <a href="https://pgp.mit.edu/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
{% endif %}
<pre>$ cat /etc/pacman.conf
[{{ repository }}]
Server = {{ link_path }}
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly</pre>
</div>
<div class="container">
<table id="packages" class="table table-striped table-hover"
data-export-options='{"fileName": "packages"}'
data-page-list="[10, 25, 50, 100, all]"
data-page-size="10"
data-pagination="true"
data-resizable="true"
data-search="true"
data-show-columns="true"
data-show-columns-search="true"
data-show-columns-toggle-all="true"
data-show-export="true"
data-show-fullscreen="true"
data-show-search-clear-button="true"
data-sortable="true"
data-sort-reset="true"
data-toggle="table">
<thead class="table-primary">
<tr>
<th data-sortable="true" data-switchable="false">package</th>
<th data-sortable="true">version</th>
<th data-sortable="true" data-visible="false">architecture</th>
<th data-sortable="true" data-visible="false">description</th>
<th data-sortable="true" data-visible="false">upstream url</th>
<th data-sortable="true" data-visible="false">licenses</th>
<th data-sortable="true" data-visible="false">groups</th>
<th data-sortable="true" data-visible="false">depends</th>
<th data-sortable="true">archive size</th>
<th data-sortable="true">installed size</th>
<th data-sortable="true">build date</th>
</tr>
</thead>
<tbody>
{% for package in packages %}
<tr>
<td><a href="{{ link_path }}/{{ package.filename }}" title="{{ package.name }}">{{ package.name }}</a></td>
<td>{{ package.version }}</td>
<td>{{ package.architecture }}</td>
<td>{{ package.description }}</td>
<td><a href="{{ package.url }}" title="{{ package.name }} upstream url">{{ package.url }}</a></td>
<td>{{ package.licenses|join("<br>"|safe) }}</td>
<td>{{ package.groups|join("<br>"|safe) }}</td>
<td>{{ package.depends|join("<br>"|safe) }}</td>
<td>{{ package.archive_size }}</td>
<td>{{ package.installed_size }}</td>
<td>{{ package.build_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
<ul class="nav">
{% if homepage is not none %}
<li><a class="nav-link" href="{{ homepage }}" title="homepage">homepage</a></li>
{% endif %}
</ul>
<ul class="nav">
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
</ul>
</footer>
</div>
{% include "utils/bootstrap-scripts.jinja2" %}
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

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

View File

@ -1,23 +0,0 @@
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/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://unpkg.com/bootstrap-table@1.18.3/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>

View File

@ -1,9 +0,0 @@
<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 href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
<style>
</style>

129
setup.py
View File

@ -1,131 +1,58 @@
from pathlib import Path
from distutils.util import convert_path
from setuptools import setup, find_packages
from typing import Any, Dict
metadata_path = Path(__file__).resolve().parent / "src/ahriman/version.py"
metadata: Dict[str, Any] = {}
with metadata_path.open() as metadata_file:
exec(metadata_file.read(), metadata) # pylint: disable=exec-used
from os import path
here = path.abspath(path.dirname(__file__))
metadata = dict()
with open(convert_path('src/ahriman/version.py')) as metadata_file:
exec(metadata_file.read(), metadata)
setup(
name="ahriman",
name='ahriman',
version=metadata["__version__"],
version=metadata['__version__'],
zip_safe=False,
description="ArcH Linux ReposItory MANager",
description='ArcHlinux ReposItory MANager',
author="ahriman team",
author_email="",
url="https://github.com/arcan1s/ahriman",
author='arcanis',
author_email='',
url='https://github.com/arcan1s/ahriman',
license="GPL3",
license='GPL3',
packages=find_packages("src"),
package_dir={"": "src"},
packages=find_packages('src'),
package_dir={'': 'src'},
dependency_links=[
],
install_requires=[
"inflection",
"passlib",
"requests",
"srcinfo",
'aur',
'srcinfo',
],
setup_requires=[
'pytest-runner',
],
tests_require=[
"pytest",
"pytest-aiohttp",
"pytest-cov",
"pytest-helpers-namespace",
"pytest-mock",
"pytest-spec",
"pytest-resource-path",
'pytest',
],
include_package_data=True,
scripts=[
"package/bin/ahriman",
'package/bin/ahriman',
],
data_files=[
("share/ahriman/settings", [
"package/share/ahriman/settings/ahriman.ini",
('/etc', ['package/etc/ahriman.ini']),
('/etc/ahriman.ini.d', ['package/etc/ahriman.ini.d/logging.ini']),
('lib/systemd/system', [
'package/lib/systemd/system/ahriman.service',
'package/lib/systemd/system/ahriman.timer'
]),
("share/ahriman/settings/ahriman.ini.d", [
"package/share/ahriman/settings/ahriman.ini.d/logging.ini",
]),
("lib/systemd/system", [
"package/lib/systemd/system/ahriman@.service",
"package/lib/systemd/system/ahriman@.timer",
"package/lib/systemd/system/ahriman-web@.service",
]),
("share/ahriman/templates", [
"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/telegram-index.jinja2",
]),
("share/ahriman/templates/build-status", [
"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",
]),
("share/ahriman/templates/static", [
"package/share/ahriman/templates/static/favicon.ico",
]),
("share/ahriman/templates/utils", [
"package/share/ahriman/templates/utils/bootstrap-scripts.jinja2",
"package/share/ahriman/templates/utils/style.jinja2",
]),
("share/man/man1", [
"docs/ahriman.1",
])
('share/ahriman', ['package/share/ahriman/index.jinja2']),
],
extras_require={
"check": [
"autopep8",
"bandit",
"mypy",
"pylint",
],
"docs": [
"Sphinx",
"argparse-manpage",
"pydeps",
"sphinx-argparse",
"sphinxcontrib-napoleon",
],
# FIXME technically this dependency is required, but in some cases we do not have access to
# the libalpm which is required in order to install the package. Thus in case if we do not
# really need to run the application we can move it to "optional" dependencies
"pacman": [
"pyalpm",
],
"s3": [
"boto3",
],
"tests": [
"pytest",
"pytest-aiohttp",
"pytest-cov",
"pytest-helpers-namespace",
"pytest-mock",
"pytest-resource-path",
"pytest-spec",
],
"web": [
"Jinja2",
"aiohttp",
"aiohttp_jinja2",
"aioauth-client",
"aiohttp_debugtoolbar",
"aiohttp_session",
"aiohttp_security",
"cryptography",
],
'html-templates': ['Jinja2'],
'test': ['coverage', 'pytest'],
},
)

View File

@ -1,7 +1,7 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021 Evgenii Alekseev.
#
# This file is part of ahriman
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify

View File

@ -1,7 +1,7 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021 Evgenii Alekseev.
#
# This file is part of ahriman
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify

View File

@ -1,7 +1,7 @@
#
# Copyright (c) 2021-2022 ahriman team.
# Copyright (c) 2021 Evgenii Alekseev.
#
# This file is part of ahriman
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
@ -18,725 +18,114 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
import sys
import tempfile
from pathlib import Path
from typing import List, TypeVar
from ahriman import version
from ahriman.application import handlers
from ahriman.models.action import Action
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package_source import PackageSource
from ahriman.models.sign_settings import SignSettings
from ahriman.models.user_access import UserAccess
__all__: List[str] = []
# this workaround is for several things
# firstly python devs don't think that is it error and asking you for workarounds https://bugs.python.org/issue41592
# secondly linters don't like when you are importing private members
# thirdly new mypy doesn't like _SubParsersAction and thinks it is a template
SubParserAction = TypeVar("SubParserAction", bound="argparse._SubParsersAction[argparse.ArgumentParser]")
def _formatter(prog: str) -> argparse.HelpFormatter:
"""
formatter for the help message
Args:
prog(str): application name
Returns:
argparse.HelpFormatter: formatter used by default
"""
return argparse.ArgumentDefaultsHelpFormatter(prog, width=120)
def _parser() -> argparse.ArgumentParser:
"""
command line parser generator
Returns:
argparse.ArgumentParser: command line parser for the application
"""
parser = argparse.ArgumentParser(prog="ahriman", description="ArcH Linux ReposItory MANager",
epilog="Argument list can also be read from file by using @ prefix.",
fromfile_prefix_chars="@", formatter_class=_formatter)
parser.add_argument("-a", "--architecture", help="target architectures (can be used multiple times)",
action="append")
parser.add_argument("-c", "--configuration", help="configuration path", type=Path, default=Path("/etc/ahriman.ini"))
parser.add_argument("--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("-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__)
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command", required=True)
_set_aur_search_parser(subparsers)
_set_help_parser(subparsers)
_set_help_commands_unsafe_parser(subparsers)
_set_key_import_parser(subparsers)
_set_package_add_parser(subparsers)
_set_package_remove_parser(subparsers)
_set_package_status_parser(subparsers)
_set_package_status_remove_parser(subparsers)
_set_package_status_update_parser(subparsers)
_set_patch_add_parser(subparsers)
_set_patch_list_parser(subparsers)
_set_patch_remove_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_update_parser(subparsers)
_set_user_add_parser(subparsers)
_set_user_list_parser(subparsers)
_set_user_remove_parser(subparsers)
_set_web_parser(subparsers)
return parser
def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for AUR search subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
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",
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("--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)
return parser
def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for listing help subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("help", help="show help message",
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,
unsafe=True, parser=_parser)
return parser
def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for listing unsafe commands
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
description="list unsafe commands as defined in default args", formatter_class=_formatter)
parser.add_argument("--command", help="instead of showing commands, just test command line for unsafe subcommand "
"and return 0 in case if command is safe and 1 otherwise")
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, no_report=True, quiet=True,
unsafe=True, parser=_parser)
return parser
def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for key import subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("key-import", help="import PGP key",
description="import PGP key from public sources to the repository user",
epilog="By default ahriman runs build process with package sources validation "
"(in case if signature and keys are available in PKGBUILD). This process will "
"fail in case if key is not known for build user. This subcommand can be used "
"in order to import the PGP key to user keychain.",
formatter_class=_formatter)
parser.add_argument("--key-server", help="key server for key import", default="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)
return parser
def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for package addition subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("package-add", aliases=["add", "package-update"], help="add package",
description="add existing or new package to the build queue",
epilog="This subcommand should be used for new package addition. It also supports flag "
"--now in case if you would like to build the package immediately. "
"You can add new package from one of supported sources: "
"1) if it is already built package you can specify the path to the archive; "
"2) you can also add built packages from the directory (e.g. during the migration "
"from another repository source); "
"3) it is also possible to add package from local PKGBUILD, but in this case it "
"will be ignored during the next automatic updates; "
"4) ahriman supports downloading archives from remote (e.g. HTTP) sources; "
"5) and finally you can add package from AUR.",
formatter_class=_formatter)
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
parser.add_argument("-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("-s", "--source", help="explicitly specify the package source for this command",
type=PackageSource, choices=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
def _set_package_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for package removal subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("package-remove", aliases=["remove"], help="remove package",
description="remove package from the repository", formatter_class=_formatter)
parser.add_argument("package", help="package name or base", nargs="+")
parser.set_defaults(handler=handlers.Remove)
return parser
def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for package status subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("package-status", aliases=["status"], help="get package status",
description="request status of the package",
epilog="This feature requests package status from the web interface if it is available.",
formatter_class=_formatter)
parser.add_argument("package", help="filter status by package base", nargs="*")
parser.add_argument("--ahriman", help="get service status itself", action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
parser.add_argument("-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)
return parser
def _set_package_status_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for package status remove subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("package-status-remove", help="remove package status",
description="remove the package from the status page",
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,
unsafe=True)
return parser
def _set_package_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for package status update subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("package-status-update", aliases=["status-update"], help="update package status",
description="update package status on the status page", formatter_class=_formatter)
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,
unsafe=True)
return parser
def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for new patch subcommand
Args:
root(SubParserAction): subparsers for the commands
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",
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)
return parser
def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for list patches subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("patch-list", help="list patch sets",
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)
return parser
def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for remove patches subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
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)
return parser
def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository backup subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-backup", help="backup repository data",
description="backup 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)
return parser
def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository check subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
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",
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)
return parser
def _set_repo_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository clean subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-clean", aliases=["clean"], help="clean local caches",
description="remove local caches",
epilog="The subcommand clears every temporary directories (builds, caches etc). Normally "
"you should not run this command manually. Also in case if you are going to clear "
"the chroot directories you will need root privileges.",
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.set_defaults(handler=handlers.Clean, quiet=True, unsafe=True)
return parser
def _set_repo_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for config subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("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)
return parser
def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository rebuild subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
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("--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 "
"ahriman instance run with web service and have run repo-update at least once.",
action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.set_defaults(handler=handlers.Rebuild)
return parser
def _set_repo_remove_unknown_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for remove unknown packages subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-remove-unknown", aliases=["remove-unknown"], help="remove unknown packages",
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.add_argument("target", help="target to generate report", nargs="*")
parser.set_defaults(handler=handlers.Report)
return parser
def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository restore subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-restore", help="restore repository data",
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)
return parser
def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for setup subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-setup", aliases=["init", "repo-init", "setup"], help="initial service configuration",
description="create initial service configuration, requires root",
epilog="Create _minimal_ configuration for the service according to provided options.",
formatter_class=_formatter)
parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
parser.add_argument("--build-command", help="build command prefix", default="ahriman")
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
type=Path, default=Path("/usr/share/devtools/pacman-extra.conf"))
parser.add_argument("--no-multilib", help="do not add multilib repository", action="store_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)
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)
return parser
def _set_repo_sign_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for sign subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-sign", aliases=["sign"], help="sign packages",
description="(re-)sign packages and repository database according to current settings",
epilog="Sign repository and/or packages as configured.",
formatter_class=_formatter)
parser.add_argument("package", help="sign only specified packages", nargs="*")
parser.set_defaults(handler=handlers.Sign)
return parser
def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository status update subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
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=[],
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.add_argument("target", help="target to sync", nargs="*")
parser.set_defaults(handler=handlers.Sync)
return parser
def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository update subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-update", aliases=["update"], help="update packages",
description="check for packages updates and run build process if requested",
formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("--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.set_defaults(handler=handlers.Update)
return parser
def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for create user subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("user-add", help="create or update user",
description="update user for web services with the given password and role. "
"In case if password was not entered it will be asked interactively",
formatter_class=_formatter)
parser.add_argument("username", help="username for web service")
parser.add_argument("--as-service", help="add user as service user", action="store_true")
parser.add_argument("-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)
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,
quiet=True, unsafe=True)
return parser
def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for user list subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("user-list", help="user known users and their access",
description="list users from the user mapping and their roles",
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
password="", quiet=True, unsafe=True)
return parser
def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for user removal subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("user-remove", help="remove user",
description="remove user from the user mapping and update the configuration",
formatter_class=_formatter)
parser.add_argument("username", help="username for web service")
parser.add_argument("-s", "--secure", help="set file permissions to user-only", action="store_true")
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, no_report=True, # nosec
password="", quiet=True, unsafe=True)
return parser
def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for web subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
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)
return parser
def run() -> None:
"""
run application instance
"""
if __name__ == "__main__":
args_parser = _parser()
args = args_parser.parse_args()
handler: handlers.Handler = args.handler
status = handler.execute(args)
sys.exit(status)
run()
import os
import ahriman.version as version
from ahriman.application.application import Application
from ahriman.core.configuration import Configuration
def _get_app(args: argparse.Namespace) -> Application:
config = _get_config(args.config)
return Application(args.architecture, config)
def _get_config(config_path: str) -> Configuration:
config = Configuration()
config.load(config_path)
config.load_logging()
return config
def _remove_lock(path: str) -> None:
if os.path.exists(path):
os.remove(path)
def add(args: argparse.Namespace) -> None:
_get_app(args).add(args.package)
def rebuild(args: argparse.Namespace) -> None:
app = _get_app(args)
packages = app.repository.packages()
app.update(packages)
def remove(args: argparse.Namespace) -> None:
_get_app(args).remove(args.package)
def report(args: argparse.Namespace) -> None:
_get_app(args).report(args.target)
def sync(args: argparse.Namespace) -> None:
_get_app(args).sync(args.target)
def update(args: argparse.Namespace) -> None:
app = _get_app(args)
log_fn = lambda line: print(line) if args.dry_run else app.logger.info(line)
packages = app.get_updates(args.no_aur, args.no_manual, args.no_vcs, log_fn)
if args.dry_run:
return
app.update(packages)
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='ahriman', description='ArcHlinux ReposItory MANager')
parser.add_argument('-a', '--architecture', help='target architecture', required=True)
parser.add_argument('-c', '--config', help='configuration path', default='/etc/ahriman.ini')
parser.add_argument('--force', help='force run, remove file lock', action='store_true')
parser.add_argument('--lock', help='lock file', default='/tmp/ahriman.lock')
parser.add_argument('-v', '--version', action='version', version=version.__version__)
subparsers = parser.add_subparsers(title='command')
add_parser = subparsers.add_parser('add', description='add package')
add_parser.add_argument('package', help='package name or archive path', nargs='+')
add_parser.set_defaults(fn=add)
check_parser = subparsers.add_parser('check', description='check for updates')
check_parser.set_defaults(fn=update, no_aur=False, no_manual=True, no_vcs=False, dry_run=True)
rebuild_parser = subparsers.add_parser('rebuild', description='rebuild whole repository')
rebuild_parser.set_defaults(fn=rebuild)
remove_parser = subparsers.add_parser('remove', description='remove package')
remove_parser.add_argument('package', help='package name', nargs='+')
remove_parser.set_defaults(fn=remove)
report_parser = subparsers.add_parser('report', description='generate report')
report_parser.add_argument('target', help='target to generate report', nargs='*')
report_parser.set_defaults(fn=report)
sync_parser = subparsers.add_parser('sync', description='sync packages to remote server')
sync_parser.add_argument('target', help='target to sync', nargs='*')
sync_parser.set_defaults(fn=sync)
update_parser = subparsers.add_parser('update', description='run updates')
update_parser.add_argument('--dry-run', help='just perform check for updates, same as check command', action='store_true')
update_parser.add_argument('--no-aur', help='do not check for AUR updates', action='store_true')
update_parser.add_argument('--no-manual', help='do not include manual updates', action='store_true')
update_parser.add_argument('--no-vcs', help='do not check VCS packages', action='store_true')
update_parser.set_defaults(fn=update)
args = parser.parse_args()
if args.force:
_remove_lock(args.lock)
if os.path.exists(args.lock):
raise RuntimeError('Another application instance is run')
if 'fn' not in args:
parser.print_help()
exit(1)
try:
open(args.lock, 'w').close()
args.fn(args)
finally:
_remove_lock(args.lock)

View File

@ -0,0 +1,90 @@
#
# Copyright (c) 2021 Evgenii Alekseev.
#
# 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 logging
import os
import shutil
from typing import Callable, List, Optional
from ahriman.core.build_tools.task import Task
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
class Application:
def __init__(self, architecture: str, config: Configuration) -> None:
self.logger = logging.getLogger('root')
self.config = config
self.architecture = architecture
self.repository = Repository(architecture, config)
def _finalize(self) -> None:
self.report()
self.sync()
def get_updates(self, no_aur: bool, no_manual: bool, no_vcs: bool,
log_fn: Callable[[str], None]) -> List[Package]:
updates = []
checked: List[str] = []
if not no_aur:
updates.extend(self.repository.updates_aur(no_vcs, checked))
if not no_manual:
updates.extend(self.repository.updates_manual(checked))
for package in updates:
log_fn(f'{package.name} = {package.version}')
return updates
def add(self, names: List[str]) -> None:
def add_manual(name: str) -> None:
package = Package.load(name, self.config.get('aur', 'url'))
Task.fetch(os.path.join(self.repository.paths.manual, package.name), package.url)
def add_archive(src: str) -> None:
dst = os.path.join(self.repository.paths.packages, os.path.basename(src))
shutil.move(src, dst)
for name in names:
if os.path.isfile(name):
add_archive(name)
else:
add_manual(name)
def remove(self, names: List[str]) -> None:
self.repository.process_remove(names)
self._finalize()
def report(self, target: Optional[List[str]] = None) -> None:
targets = target or None
self.repository.process_report(targets)
def sync(self, target: Optional[List[str]] = None) -> None:
targets = target or None
self.repository.process_sync(targets)
def update(self, updates: List[Package]) -> None:
packages = self.repository.process_build(updates)
self.repository.process_update(packages)
self._finalize()

View File

@ -1,76 +0,0 @@
#
# 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/>.
#
from typing import Set
from ahriman.application.application.application_packages import ApplicationPackages
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.models.result import Result
class Application(ApplicationPackages, ApplicationRepository):
"""
base application class
Examples:
This class groups ``Repository`` methods into specific method which process all supposed actions caused by
underlying action. E.g.::
>>> from ahriman.core.configuration import Configuration
>>> from ahriman.models.package_source import PackageSource
>>>
>>> configuration = Configuration()
>>> application = Application("x86_64", configuration, no_report=False, 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 for specified packages
>>> application.update(updates)
In case if specific actions or their order are required, the direct access to ``Repository`` must
be used instead.
"""
def _finalize(self, result: Result) -> None:
"""
generate report and sync to remote server
Args:
result(Result): build result
"""
self.report([], result)
self.sync([], result.success)
def _known_packages(self) -> Set[str]:
"""
load packages from repository and pacman repositories
Returns:
Set[str]: list of known packages
"""
known_packages: Set[str] = set()
# local set
for base in self.repository.packages():
for package, properties in base.packages.items():
known_packages.add(package)
known_packages.update(properties.provides)
known_packages.update(self.repository.pacman.all_packages())
return known_packages

View File

@ -1,188 +0,0 @@
#
# 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 requests
import shutil
from pathlib import Path
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.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.result import Result
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
Returns:
Set[str]: list of known packages
Raises:
NotImplementedError: not implemented method
"""
raise NotImplementedError
def _add_archive(self, source: str, *_: Any) -> None:
"""
add package from archive
Args:
source(str): path to package archive
"""
local_path = Path(source)
dst = self.repository.paths.packages / local_path.name
shutil.copy(local_path, dst)
def _add_aur(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None:
"""
add package from AUR
Args:
source(str): package base name
known_packages(Set[str]): list of packages which are known by the service
without_dependencies(bool): if set, dependency check will be disabled
"""
package = Package.from_aur(source, self.repository.pacman)
self.database.build_queue_insert(package)
self.database.remote_update(package)
with tmpdir() as local_path:
Sources.load(local_path, package, self.database.patches_get(package.base), self.repository.paths)
self._process_dependencies(local_path, known_packages, without_dependencies)
def _add_directory(self, source: str, *_: Any) -> None:
"""
add packages from directory
Args:
source(str): path to local directory
"""
local_path = Path(source)
for full_path in filter(package_like, local_path.iterdir()):
self._add_archive(str(full_path))
def _add_local(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None:
"""
add package from local PKGBUILDs
Args:
source(str): path to directory with local source files
known_packages(Set[str]): list of packages which are known by the service
without_dependencies(bool): if set, dependency check will be disabled
"""
source_dir = Path(source)
package = Package.from_build(source_dir)
cache_dir = self.repository.paths.cache_for(package.base)
shutil.copytree(source_dir, cache_dir) # copy package to store in caches
Sources.init(cache_dir) # we need to run init command in directory where we do have permissions
self.database.build_queue_insert(package)
self._process_dependencies(cache_dir, known_packages, without_dependencies)
def _add_remote(self, source: str, *_: Any) -> None:
"""
add package from remote sources (e.g. HTTP)
Args:
source(str): remote URL of the package archive
"""
dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
response = requests.get(source, stream=True)
response.raise_for_status()
with dst.open("wb") as local_file:
for chunk in response.iter_content(chunk_size=1024):
local_file.write(chunk)
def _add_repository(self, source: str, *_: Any) -> None:
"""
add package from official repository
Args:
source(str): package base name
"""
package = Package.from_official(source, self.repository.pacman)
self.database.build_queue_insert(package)
self.database.remote_update(package)
# repository packages must not depend on unknown packages, thus we are not going to process dependencies
def _process_dependencies(self, local_path: Path, known_packages: Set[str], without_dependencies: bool) -> None:
"""
process package dependencies
Args:
local_path(Path): path to local package sources (i.e. cloned AUR repository)
known_packages(Set[str]): list of packages which are known by the service
without_dependencies(bool): if set, dependency check will be disabled
"""
if without_dependencies:
return
dependencies = Package.dependencies(local_path)
self.add(dependencies.difference(known_packages), PackageSource.AUR, without_dependencies)
def add(self, names: Iterable[str], source: PackageSource, without_dependencies: bool) -> None:
"""
add packages for the next build
Args:
names(Iterable[str]): list of package bases to add
source(PackageSource): package source to add
without_dependencies(bool): if set, dependency check will be disabled
"""
known_packages = self._known_packages() # speedup dependencies processing
for name in names:
resolved_source = source.resolve(name)
fn = getattr(self, f"_add_{resolved_source.value}")
fn(name, known_packages, without_dependencies)
def remove(self, names: Iterable[str]) -> None:
"""
remove packages from repository
Args:
names(Iterable[str]): list of packages (either base or name) to remove
"""
self.repository.process_remove(names)
self._finalize(Result())

View File

@ -1,53 +0,0 @@
#
# 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 logging
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
class ApplicationProperties:
"""
application base properties class
Attributes:
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:
"""
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
"""
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)

View File

@ -1,212 +0,0 @@
#
# 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 shutil
from pathlib import Path
from typing import Callable, Iterable, List
from ahriman.application.application.application_properties import ApplicationProperties
from ahriman.core.build_tools.sources import Sources
from ahriman.core.formatters import UpdatePrinter
from ahriman.core.tree import Tree
from ahriman.models.package import Package
from ahriman.models.result import Result
class ApplicationRepository(ApplicationProperties):
"""
repository 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 clean(self, cache: bool, chroot: bool, manual: bool, packages: bool) -> None:
"""
run all clean methods. Warning: some functions might not be available under non-root
Args:
cache(bool): clear directory with package caches
chroot(bool): clear build chroot
manual(bool): clear directory with manually added packages
packages(bool): clear directory with built packages
"""
if cache:
self.repository.clear_cache()
if chroot:
self.repository.clear_chroot()
if manual:
self.repository.clear_queue()
if packages:
self.repository.clear_packages()
def report(self, target: Iterable[str], result: Result) -> None:
"""
generate report
Args:
target(Iterable[str]): list of targets to run (e.g. html)
result(Result): build result
"""
targets = target or None
self.repository.process_report(targets, result)
def sign(self, packages: Iterable[str]) -> None:
"""
sign packages and repository
Args:
packages(Iterable[str]): only sign specified packages
"""
# copy to prebuilt directory
for package in self.repository.packages():
# no one requested this package
if packages and package.base not in packages:
continue
for archive in package.packages.values():
if archive.filepath is None:
self.logger.warning("filepath is empty for %s", package.base)
continue # avoid mypy warning
src = self.repository.paths.repository / archive.filepath
dst = self.repository.paths.packages / archive.filepath
shutil.copy(src, dst)
# run generic update function
self.update([])
# sign repository database if set
self.repository.sign.process_sign_repository(self.repository.repo.repo_path)
self._finalize(Result())
def sync(self, target: Iterable[str], built_packages: Iterable[Package]) -> None:
"""
sync to remote server
Args:
target(Iterable[str]): list of targets to run (e.g. s3)
built_packages(Iterable[Package]): list of packages which has just been built
"""
targets = target or None
self.repository.process_sync(targets, built_packages)
def unknown(self) -> List[str]:
"""
get packages which were not found in AUR
Returns:
List[str]: unknown package archive list
"""
def has_local(probe: Package) -> bool:
cache_dir = self.repository.paths.cache_for(probe.base)
return cache_dir.is_dir() and not Sources.has_remotes(cache_dir)
def unknown_aur(probe: Package) -> List[str]:
packages: List[str] = []
for single in probe.packages:
try:
_ = Package.from_aur(single, self.repository.pacman)
except Exception:
packages.append(single)
return packages
def unknown_local(probe: Package) -> List[str]:
cache_dir = self.repository.paths.cache_for(probe.base)
local = Package.from_build(cache_dir)
packages = set(probe.packages.keys()).difference(local.packages.keys())
return list(packages)
result = []
for package in self.repository.packages():
if has_local(package):
result.extend(unknown_local(package)) # there is local package
else:
result.extend(unknown_aur(package)) # local package not found
return result
def update(self, updates: Iterable[Package]) -> Result:
"""
run package updates
Args:
updates(Iterable[Package]): list of packages to update
Returns:
Result: update result
"""
def process_update(paths: Iterable[Path], result: Result) -> None:
if not paths:
return # don't need to process if no update supplied
update_result = self.repository.process_update(paths)
self._finalize(result.merge(update_result))
# process built packages
build_result = Result()
packages = self.repository.packages_built()
process_update(packages, build_result)
# process manual packages
tree = Tree.load(updates, self.repository.paths, self.database)
for num, level in enumerate(tree.levels()):
self.logger.info("processing level #%i %s", num, [package.base for package in level])
build_result = self.repository.process_build(level)
packages = self.repository.packages_built()
process_update(packages, build_result)
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]:
"""
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
log_fn(Callable[[str], None]): logger function to log updates
Returns:
List[Package]: list of out-of-dated packages
"""
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:
updates.update({package.base: package for package in self.repository.updates_local()})
if not no_manual:
updates.update({package.base: package for package in self.repository.updates_manual()})
local_versions = {package.base: package.version for package in self.repository.packages()}
updated_packages = [package for _, package in sorted(updates.items())]
for package in updated_packages:
UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ")
return updated_packages

View File

@ -1,43 +0,0 @@
#
# 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/>.
#
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.dump import Dump
from ahriman.application.handlers.help import Help
from ahriman.application.handlers.key_import import KeyImport
from ahriman.application.handlers.patch import Patch
from ahriman.application.handlers.rebuild import Rebuild
from ahriman.application.handlers.remove import Remove
from ahriman.application.handlers.remove_unknown import RemoveUnknown
from ahriman.application.handlers.report import Report
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.sign import Sign
from ahriman.application.handlers.status import Status
from ahriman.application.handlers.status_update import StatusUpdate
from ahriman.application.handlers.sync import Sync
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.web import Web

View File

@ -1,54 +0,0 @@
#
# 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
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
class Add(Handler):
"""
add packages handler
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
"""
application = Application(architecture, configuration, no_report, unsafe)
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)
result = application.update(packages)
Add.check_if_empty(args.exit_code, result.is_empty)

View File

@ -1,86 +0,0 @@
#
# 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 pwd
from pathlib import Path
from tarfile import TarFile
from typing import Set, Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
class Backup(Handler):
"""
backup packages 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:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
"""
backup_paths = Backup.get_paths(configuration)
with TarFile(args.path, mode="w") as archive: # well we don't actually use compression
for backup_path in backup_paths:
archive.add(backup_path)
@staticmethod
def get_paths(configuration: Configuration) -> Set[Path]:
"""
extract paths to backup
Args:
configuration(Configuration): configuration instance
Returns:
Set[Path]: map of the filesystem paths
"""
paths = set(configuration.include.glob("*.ini"))
root, _ = configuration.check_loaded()
paths.add(root) # the configuration itself
paths.add(SQLite.database_path(configuration)) # database
# local caches
repository_paths = configuration.repository_paths
if repository_paths.cache.is_dir():
paths.add(repository_paths.cache)
# gnupg home with imported keys
uid, _ = repository_paths.root_owner
system_user = pwd.getpwuid(uid)
gnupg_home = Path(system_user.pw_dir) / ".gnupg"
if gnupg_home.is_dir():
paths.add(gnupg_home)
return paths

View File

@ -1,48 +0,0 @@
#
# 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
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
class Clean(Handler):
"""
clean caches handler
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
"""
Application(architecture, configuration, no_report, unsafe).clean(
args.cache, args.chroot, args.manual, args.packages)

View File

@ -1,51 +0,0 @@
#
# 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
from typing import Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ConfigurationPrinter
class Dump(Handler):
"""
dump configuration 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:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
"""
dump = configuration.dump()
for section, values in sorted(dump.items()):
ConfigurationPrinter(section, values).print(verbose=False, separator=" = ")

View File

@ -1,168 +0,0 @@
#
# 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/>.
#
from __future__ import annotations
import argparse
import logging
from multiprocessing import Pool
from typing import List, Type
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode, MissingArchitecture, MultipleArchitectures
from ahriman.models.repository_paths import RepositoryPaths
class Handler:
"""
base handler class for command callbacks
Attributes:
ALLOW_AUTO_ARCHITECTURE_RUN(bool): (class attribute) allow defining architecture from existing repositories
ALLOW_MULTI_ARCHITECTURE_RUN(bool): (class attribute) allow running with multiple architectures
Examples:
Wrapper for all command line actions, though each derived class implements ``run`` method, it usually must not
be called directly. The recommended way is to call ``execute`` class method, e.g.::
>>> from ahriman.application.handlers import Add
>>> Add.execute(args)
"""
ALLOW_AUTO_ARCHITECTURE_RUN = True
ALLOW_MULTI_ARCHITECTURE_RUN = True
@classmethod
def architectures_extract(cls: Type[Handler], args: argparse.Namespace) -> List[str]:
"""
get known architectures
Args:
args(argparse.Namespace): command line args
Returns:
List[str]: list of architectures for which tree is created
Raises:
MissingArchitecture: if no architecture set and automatic detection is not allowed or failed
"""
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)
if args.architecture: # architecture is specified explicitly
return sorted(set(args.architecture))
config = Configuration()
config.load(args.configuration)
# wtf???
root = config.getpath("repository", "root") # pylint: disable=assignment-from-no-return
architectures = RepositoryPaths.known_architectures(root)
if not architectures: # well we did not find anything
raise MissingArchitecture(args.command)
return sorted(architectures)
@classmethod
def call(cls: Type[Handler], args: argparse.Namespace, architecture: str) -> bool:
"""
additional function to wrap all calls for multiprocessing library
Args:
args(argparse.Namespace): command line args
architecture(str): repository architecture
Returns:
bool: True on success, False otherwise
"""
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)
return True
except ExitCode:
return False
except Exception:
# we are basically always want to print error to stderr instead of default logger
logging.getLogger("stderr").exception("process exception")
return False
@classmethod
def execute(cls: Type[Handler], args: argparse.Namespace) -> int:
"""
execute function for all aru
Args:
args(argparse.Namespace): command line args
Returns:
int: 0 on success, 1 otherwise
Raises:
MultipleArchitectures: if more than one architecture supplied and no multi architecture supported
"""
architectures = cls.architectures_extract(args)
# 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)
with Pool(len(architectures)) as pool:
result = pool.starmap(
cls.call, [(args, architecture) for architecture in architectures])
else:
result = [cls.call(args, architectures.pop())]
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:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
Raises:
NotImplementedError: not implemented method
"""
raise NotImplementedError
@staticmethod
def check_if_empty(enabled: bool, predicate: bool) -> None:
"""
check condition and flag and raise ExitCode exception in case if it is enabled and condition match
Args:
enabled(bool): if False no check will be performed
predicate(bool): indicates condition on which exception should be thrown
Raises:
ExitCode: if result is empty and check is enabled
"""
if enabled and predicate:
raise ExitCode()

View File

@ -1,52 +0,0 @@
#
# 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
from typing import Type
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
class Help(Handler):
"""
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:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
"""
parser: argparse.ArgumentParser = args.parser()
if args.command is None:
parser.parse_args(["--help"])
else:
parser.parse_args([args.command, "--help"])

View File

@ -1,50 +0,0 @@
#
# 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
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
class KeyImport(Handler):
"""
key import packages 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:
"""
callback for command line
Args:
args(argparse.Namespace): command line 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
"""
Application(architecture, configuration, no_report, unsafe).repository.sign.key_import(
args.key_server, args.key)

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