mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 23:01:44 +00:00
Compare commits
4 Commits
master
...
027a3a8fb6
Author | SHA1 | Date | |
---|---|---|---|
027a3a8fb6 | |||
f41b69f42a | |||
930eccc55a | |||
32f99f7f36 |
6
.bandit-test.yml
Normal file
6
.bandit-test.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
skips:
|
||||||
|
- B101
|
||||||
|
- B104
|
||||||
|
- B105
|
||||||
|
- B106
|
||||||
|
- B404
|
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@ -21,18 +21,18 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: docker/setup-qemu-action@v3
|
- uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Login to docker hub
|
- name: Login to docker hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to github container registry
|
- name: Login to github container registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@ -40,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Extract docker metadata
|
- name: Extract docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v3
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
arcan1s/ahriman
|
arcan1s/ahriman
|
||||||
@ -50,7 +50,7 @@ jobs:
|
|||||||
type=edge
|
type=edge
|
||||||
|
|
||||||
- name: Build an image and push
|
- name: Build an image and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
|
2
.github/workflows/regress.yml
vendored
2
.github/workflows/regress.yml
vendored
@ -37,6 +37,8 @@ jobs:
|
|||||||
- repo:/var/lib/ahriman
|
- repo:/var/lib/ahriman
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- run: pacman -Sy
|
- run: pacman -Sy
|
||||||
|
|
||||||
- name: Init repository
|
- name: Init repository
|
||||||
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Extract version
|
- name: Extract version
|
||||||
id: version
|
id: version
|
||||||
@ -27,7 +27,8 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
filter: 'Release \d+\.\d+\.\d+'
|
filter: 'Release \d+\.\d+\.\d+'
|
||||||
|
|
||||||
- uses: ConorMacBride/install-package@v1.1.0
|
- name: Install dependencies
|
||||||
|
uses: ConorMacBride/install-package@v1.1.0
|
||||||
with:
|
with:
|
||||||
apt: tox
|
apt: tox
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ jobs:
|
|||||||
VERSION: ${{ steps.version.outputs.VERSION }}
|
VERSION: ${{ steps.version.outputs.VERSION }}
|
||||||
|
|
||||||
- name: Publish release
|
- name: Publish release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
body: |
|
body: |
|
||||||
${{ steps.changelog.outputs.compareurl }}
|
${{ steps.changelog.outputs.compareurl }}
|
||||||
|
4
.github/workflows/setup.yml
vendored
4
.github/workflows/setup.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
- ${{ github.workspace }}:/build
|
- ${{ github.workspace }}:/build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup the minimal service in arch linux container
|
- name: Setup the minimal service in arch linux container
|
||||||
run: .github/workflows/setup.sh minimal
|
run: .github/workflows/setup.sh minimal
|
||||||
@ -40,7 +40,7 @@ jobs:
|
|||||||
options: --privileged -w /build
|
options: --privileged -w /build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup the service in arch linux container
|
- name: Setup the service in arch linux container
|
||||||
run: .github/workflows/setup.sh
|
run: .github/workflows/setup.sh
|
||||||
|
10
.github/workflows/tests.sh
vendored
Executable file
10
.github/workflows/tests.sh
vendored
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Install dependencies and run test in container
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
pacman --noconfirm -Syyu base-devel python-tox
|
||||||
|
|
||||||
|
# run test and check targets
|
||||||
|
tox
|
15
.github/workflows/tests.yml
vendored
15
.github/workflows/tests.yml
vendored
@ -26,16 +26,7 @@ jobs:
|
|||||||
- ${{ github.workspace }}:/build
|
- ${{ github.workspace }}:/build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- run: pacman --noconfirm -Syu base-devel git python-tox
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- run: git config --global --add safe.directory *
|
- name: Run check and tests in arch linux container
|
||||||
|
run: .github/workflows/tests.sh
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Run check and tests
|
|
||||||
run: tox
|
|
||||||
|
|
||||||
- name: Generate documentation and check if there are untracked changes
|
|
||||||
run: |
|
|
||||||
tox -e docs
|
|
||||||
[ -z "$(git status --porcelain docs/*.rst)" ]
|
|
||||||
|
45
.pylint.toml
45
.pylint.toml
@ -1,45 +0,0 @@
|
|||||||
[tool.pylint.main]
|
|
||||||
init-hook = "sys.path.append('tools')"
|
|
||||||
load-plugins = [
|
|
||||||
"pylint.extensions.docparams",
|
|
||||||
"pylint.extensions.bad_builtin",
|
|
||||||
"pylint_plugins.definition_order",
|
|
||||||
"pylint_plugins.import_order",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.pylint.classes]
|
|
||||||
bad-functions = [
|
|
||||||
"print",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.pylint.design]
|
|
||||||
max-parents = 15
|
|
||||||
|
|
||||||
[tool.pylint."messages control"]
|
|
||||||
disable = [
|
|
||||||
"raw-checker-failed",
|
|
||||||
"bad-inline-option",
|
|
||||||
"locally-disabled",
|
|
||||||
"file-ignored",
|
|
||||||
"suppressed-message",
|
|
||||||
"useless-suppression",
|
|
||||||
"deprecated-pragma",
|
|
||||||
"use-symbolic-message-instead",
|
|
||||||
"use-implicit-booleaness-not-comparison-to-string",
|
|
||||||
"use-implicit-booleaness-not-comparison-to-zero",
|
|
||||||
"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-exception-caught",
|
|
||||||
"fixme",
|
|
||||||
"too-many-arguments",
|
|
||||||
"duplicate-code",
|
|
||||||
"cyclic-import",
|
|
||||||
"too-many-positional-arguments",
|
|
||||||
]
|
|
651
.pylintrc
Normal file
651
.pylintrc
Normal file
@ -0,0 +1,651 @@
|
|||||||
|
[MAIN]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Clear in-memory caches upon conclusion of linting. Useful if running pylint
|
||||||
|
# in a server-like mode.
|
||||||
|
clear-cache-post-run=no
|
||||||
|
|
||||||
|
# Load and enable all available extensions. Use --list-extensions to see a list
|
||||||
|
# all available extensions.
|
||||||
|
#enable-all-extensions=
|
||||||
|
|
||||||
|
# In error mode, messages with a category besides ERROR or FATAL are
|
||||||
|
# suppressed, and no reports are done by default. Error mode is compatible with
|
||||||
|
# disabling specific errors.
|
||||||
|
#errors-only=
|
||||||
|
|
||||||
|
# Always return a 0 (non-error) status code, even if lint errors are found.
|
||||||
|
# This is primarily useful in continuous integration scripts.
|
||||||
|
#exit-zero=
|
||||||
|
|
||||||
|
# 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-allow-list=
|
||||||
|
|
||||||
|
# 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. (This is an alternative name to extension-pkg-allow-list
|
||||||
|
# for backward compatibility.)
|
||||||
|
extension-pkg-whitelist=
|
||||||
|
|
||||||
|
# Return non-zero exit code if any of these messages/categories are detected,
|
||||||
|
# even if score is above --fail-under value. Syntax same as enable. Messages
|
||||||
|
# specified are enabled, while categories only check already-enabled messages.
|
||||||
|
fail-on=
|
||||||
|
|
||||||
|
# Specify a score threshold under which the program will exit with error.
|
||||||
|
fail-under=10
|
||||||
|
|
||||||
|
# Interpret the stdin as a python script, whose filename needs to be passed as
|
||||||
|
# the module_or_package argument.
|
||||||
|
#from-stdin=
|
||||||
|
|
||||||
|
# Files or directories to be skipped. They should be base names, not paths.
|
||||||
|
ignore=CVS
|
||||||
|
|
||||||
|
# Add files or directories matching the regular expressions patterns to the
|
||||||
|
# ignore-list. The regex matches against paths and can be in Posix or Windows
|
||||||
|
# format. Because '\\' represents the directory delimiter on Windows systems,
|
||||||
|
# it can't be used as an escape character.
|
||||||
|
ignore-paths=
|
||||||
|
|
||||||
|
# Files or directories matching the regular expression patterns are skipped.
|
||||||
|
# The regex matches against base names, not paths. The default value ignores
|
||||||
|
# Emacs file locks
|
||||||
|
ignore-patterns=^\.#
|
||||||
|
|
||||||
|
# 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=
|
||||||
|
|
||||||
|
# Python code to execute, usually for sys.path manipulation such as
|
||||||
|
# pygtk.require().
|
||||||
|
init-hook='import sys; sys.path.append("pylint_plugins")'
|
||||||
|
|
||||||
|
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||||
|
# number of processors available to use, and will cap the count on Windows to
|
||||||
|
# avoid hangs.
|
||||||
|
jobs=1
|
||||||
|
|
||||||
|
# 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=pylint.extensions.docparams,
|
||||||
|
pylint.extensions.bad_builtin,
|
||||||
|
definition_order,
|
||||||
|
import_order,
|
||||||
|
|
||||||
|
# Pickle collected data for later comparisons.
|
||||||
|
persistent=yes
|
||||||
|
|
||||||
|
# Minimum Python version to use for version dependent checks. Will default to
|
||||||
|
# the version used to run pylint.
|
||||||
|
py-version=3.11
|
||||||
|
|
||||||
|
# Discover python modules and packages in the file system subtree.
|
||||||
|
recursive=no
|
||||||
|
|
||||||
|
# Add paths to the list of the source roots. Supports globbing patterns. The
|
||||||
|
# source root is an absolute path or a path relative to the current working
|
||||||
|
# directory used to determine a package namespace for modules located under the
|
||||||
|
# source root.
|
||||||
|
source-roots=
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# In verbose mode, extra non-checker-related info will be displayed.
|
||||||
|
#verbose=
|
||||||
|
|
||||||
|
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Naming style matching correct argument names.
|
||||||
|
argument-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct argument names. Overrides argument-
|
||||||
|
# naming-style. If left empty, argument names will be checked with the set
|
||||||
|
# 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. If left empty, attribute names will be checked with the set naming
|
||||||
|
# style.
|
||||||
|
#attr-rgx=
|
||||||
|
|
||||||
|
bad-functions=print,
|
||||||
|
|
||||||
|
# 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. If left empty, class attribute names will be checked
|
||||||
|
# with the set naming style.
|
||||||
|
#class-attribute-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct class constant names.
|
||||||
|
class-const-naming-style=UPPER_CASE
|
||||||
|
|
||||||
|
# Regular expression matching correct class constant names. Overrides class-
|
||||||
|
# const-naming-style. If left empty, class constant names will be checked with
|
||||||
|
# the set naming style.
|
||||||
|
#class-const-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct class names.
|
||||||
|
class-naming-style=PascalCase
|
||||||
|
|
||||||
|
# Regular expression matching correct class names. Overrides class-naming-
|
||||||
|
# style. If left empty, class names will be checked with the set 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. If left empty, constant names will be checked with the set 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. If left empty, function names will be checked with the set
|
||||||
|
# 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. If left empty, inline iteration names will be checked
|
||||||
|
# with the set 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. If left empty, method names will be checked with the set 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. If left empty, module names will be checked with the set 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
|
||||||
|
|
||||||
|
# Regular expression matching correct type alias names. If left empty, type
|
||||||
|
# alias names will be checked with the set naming style.
|
||||||
|
#typealias-rgx=
|
||||||
|
|
||||||
|
# Regular expression matching correct type variable names. If left empty, type
|
||||||
|
# variable names will be checked with the set naming style.
|
||||||
|
#typevar-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct variable names.
|
||||||
|
variable-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct variable names. Overrides variable-
|
||||||
|
# naming-style. If left empty, variable names will be checked with the set
|
||||||
|
# naming style.
|
||||||
|
#variable-rgx=
|
||||||
|
|
||||||
|
|
||||||
|
[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,
|
||||||
|
asyncSetUp,
|
||||||
|
__post_init__
|
||||||
|
|
||||||
|
# List of member names, which should be excluded from the protected access
|
||||||
|
# warning.
|
||||||
|
exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit
|
||||||
|
|
||||||
|
# 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=mcs
|
||||||
|
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
# List of regular expressions of class ancestor names to ignore when counting
|
||||||
|
# public methods (see R0903)
|
||||||
|
exclude-too-few-public-methods=
|
||||||
|
|
||||||
|
# List of qualified class names to ignore when counting class parents (see
|
||||||
|
# R0901)
|
||||||
|
ignored-parents=
|
||||||
|
|
||||||
|
# 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=15
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
[EXCEPTIONS]
|
||||||
|
|
||||||
|
# Exceptions that will emit a warning when caught.
|
||||||
|
overgeneral-exceptions=builtins.BaseException,builtins.Exception
|
||||||
|
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
# List of modules that can be imported at any level, not just the top level
|
||||||
|
# one.
|
||||||
|
allow-any-import-level=
|
||||||
|
|
||||||
|
# Allow explicit reexports by alias from a package __init__.
|
||||||
|
allow-reexport-from-package=no
|
||||||
|
|
||||||
|
# Allow wildcard imports from modules that define __all__.
|
||||||
|
allow-wildcard-with-all=no
|
||||||
|
|
||||||
|
# Deprecated modules which should not be used, separated by a comma.
|
||||||
|
deprecated-modules=
|
||||||
|
|
||||||
|
# Output a graph (.gv or any supported image format) of external dependencies
|
||||||
|
# to the given file (report RP0402 must not be disabled).
|
||||||
|
ext-import-graph=
|
||||||
|
|
||||||
|
# Output a graph (.gv or any supported image format) of all (i.e. internal and
|
||||||
|
# external) dependencies to the given file (report RP0402 must not be
|
||||||
|
# disabled).
|
||||||
|
import-graph=
|
||||||
|
|
||||||
|
# Output a graph (.gv or any supported image format) of internal dependencies
|
||||||
|
# to 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=
|
||||||
|
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||||
|
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
|
||||||
|
# UNDEFINED.
|
||||||
|
confidence=HIGH,
|
||||||
|
CONTROL_FLOW,
|
||||||
|
INFERENCE,
|
||||||
|
INFERENCE_FAILURE,
|
||||||
|
UNDEFINED
|
||||||
|
|
||||||
|
# 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 re-enable 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=raw-checker-failed,
|
||||||
|
bad-inline-option,
|
||||||
|
locally-disabled,
|
||||||
|
file-ignored,
|
||||||
|
suppressed-message,
|
||||||
|
useless-suppression,
|
||||||
|
deprecated-pragma,
|
||||||
|
use-symbolic-message-instead,
|
||||||
|
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,
|
||||||
|
fixme,
|
||||||
|
too-many-arguments,
|
||||||
|
duplicate-code,
|
||||||
|
cyclic-import,
|
||||||
|
too-many-positional-arguments,
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
[METHOD_ARGS]
|
||||||
|
|
||||||
|
# List of qualified names (i.e., library.method) which require a timeout
|
||||||
|
# parameter e.g. 'requests.api.get,requests.api.post'
|
||||||
|
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
|
||||||
|
|
||||||
|
|
||||||
|
[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=
|
||||||
|
|
||||||
|
|
||||||
|
[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,argparse.parse_error
|
||||||
|
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
# Python expression which should return a score less than or equal to 10. You
|
||||||
|
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
|
||||||
|
# 'convention', and 'info' 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=max(0, 0 if fatal else 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=
|
||||||
|
|
||||||
|
# Tells whether to display a full report or only the messages.
|
||||||
|
reports=no
|
||||||
|
|
||||||
|
# Activate the evaluation score.
|
||||||
|
score=yes
|
||||||
|
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Comments are removed from the similarity computation
|
||||||
|
ignore-comments=yes
|
||||||
|
|
||||||
|
# Docstrings are removed from the similarity computation
|
||||||
|
ignore-docstrings=yes
|
||||||
|
|
||||||
|
# Imports are removed from the similarity computation
|
||||||
|
ignore-imports=yes
|
||||||
|
|
||||||
|
# Signatures are removed from the similarity computation
|
||||||
|
ignore-signatures=yes
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
|
||||||
|
[SPELLING]
|
||||||
|
|
||||||
|
# Limits count of emitted suggestions for spelling mistakes.
|
||||||
|
max-spelling-suggestions=4
|
||||||
|
|
||||||
|
# Spelling dictionary name. No available dictionaries : You need to install
|
||||||
|
# both the python package and the system dependency for enchant to work..
|
||||||
|
spelling-dict=
|
||||||
|
|
||||||
|
# List of comma separated words that should be considered directives if they
|
||||||
|
# appear at the beginning of a comment and should not be checked.
|
||||||
|
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
|
||||||
|
[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 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 symbolic message names to ignore for Mixin members.
|
||||||
|
ignored-checks-for-mixins=no-member,
|
||||||
|
not-async-context-manager,
|
||||||
|
not-context-manager,
|
||||||
|
attribute-defined-outside-init
|
||||||
|
|
||||||
|
# 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,argparse.Namespace
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Regex pattern to define which classes are considered mixins.
|
||||||
|
mixin-class-rgx=.*[Mm]ixin
|
||||||
|
|
||||||
|
# List of decorators that change the signature of a decorated function.
|
||||||
|
signature-mutators=
|
||||||
|
|
||||||
|
|
||||||
|
[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 names allowed to shadow builtins
|
||||||
|
allowed-redefined-builtins=
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
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
|
@ -1,5 +0,0 @@
|
|||||||
[pytest]
|
|
||||||
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
|
||||||
asyncio_default_fixture_loop_scope = function
|
|
||||||
asyncio_mode = auto
|
|
||||||
spec_test_format = {result} {docstring_summary}
|
|
2621
docs/_static/architecture.dot
vendored
2621
docs/_static/architecture.dot
vendored
File diff suppressed because it is too large
Load Diff
@ -413,11 +413,10 @@ Web application
|
|||||||
Web application requires the following python packages to be installed:
|
Web application requires the following python packages to be installed:
|
||||||
|
|
||||||
* Core part requires ``aiohttp`` (application itself), ``aiohttp_jinja2`` and ``Jinja2`` (HTML generation from templates).
|
* Core part requires ``aiohttp`` (application itself), ``aiohttp_jinja2`` and ``Jinja2`` (HTML generation from templates).
|
||||||
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation, optional), ``aiohttp_cors`` (CORS support, required by documentation).
|
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation), ``aiohttp_cors`` (CORS support, required by documentation).
|
||||||
* In addition, authorization feature requires ``aiohttp_security``, ``aiohttp_session`` and ``cryptography``.
|
* In addition, authorization feature requires ``aiohttp_security``, ``aiohttp_session`` and ``cryptography``.
|
||||||
* In addition to base authorization dependencies, OAuth2 also requires ``aioauth-client`` library.
|
* In addition to base authorization dependencies, OAuth2 also requires ``aioauth-client`` library.
|
||||||
* In addition if you would like to disable authorization for local access (recommended way in order to run the application itself with reporting support), the ``requests-unixsocket2`` library is required.
|
* In addition if you would like to disable authorization for local access (recommended way in order to run the application itself with reporting support), the ``requests-unixsocket2`` library is required.
|
||||||
* Application metrics will be automatically enabled after installing ``aiohttp-openmetrics`` package.
|
|
||||||
|
|
||||||
Middlewares
|
Middlewares
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
# This file was autogenerated by uv via the following command:
|
# This file was autogenerated by uv via the following command:
|
||||||
# uv pip compile --group pyproject.toml:docs --extra s3 --extra validator --extra web --output-file docs/requirements.txt pyproject.toml
|
# uv pip compile --group ../pyproject.toml:docs --extra s3 --extra validator --extra web --output-file ../docs/requirements.txt ../pyproject.toml
|
||||||
aiohappyeyeballs==2.6.1
|
aiohappyeyeballs==2.6.1
|
||||||
# via aiohttp
|
# via aiohttp
|
||||||
aiohttp==3.11.18
|
aiohttp==3.11.18
|
||||||
# via
|
# via
|
||||||
# ahriman (pyproject.toml)
|
# ahriman (../pyproject.toml)
|
||||||
# aiohttp-cors
|
# aiohttp-cors
|
||||||
# aiohttp-jinja2
|
# aiohttp-jinja2
|
||||||
aiohttp-cors==0.8.1
|
aiohttp-cors==0.8.1
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
aiohttp-jinja2==1.6
|
aiohttp-jinja2==1.6
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
aiosignal==1.3.2
|
aiosignal==1.3.2
|
||||||
# via aiohttp
|
# via aiohttp
|
||||||
alabaster==1.0.0
|
alabaster==1.0.0
|
||||||
# via sphinx
|
# via sphinx
|
||||||
argparse-manpage==4.6
|
argparse-manpage==4.6
|
||||||
# via ahriman (pyproject.toml:docs)
|
# via ahriman (../pyproject.toml:docs)
|
||||||
attrs==25.3.0
|
attrs==25.3.0
|
||||||
# via aiohttp
|
# via aiohttp
|
||||||
babel==2.17.0
|
babel==2.17.0
|
||||||
# via sphinx
|
# via sphinx
|
||||||
bcrypt==4.3.0
|
bcrypt==4.3.0
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
boto3==1.38.11
|
boto3==1.38.11
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
botocore==1.38.11
|
botocore==1.38.11
|
||||||
# via
|
# via
|
||||||
# boto3
|
# boto3
|
||||||
# s3transfer
|
# s3transfer
|
||||||
cerberus==1.3.7
|
cerberus==1.3.7
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
certifi==2025.4.26
|
certifi==2025.4.26
|
||||||
# via requests
|
# via requests
|
||||||
charset-normalizer==3.4.2
|
charset-normalizer==3.4.2
|
||||||
@ -51,7 +51,7 @@ idna==3.10
|
|||||||
imagesize==1.4.1
|
imagesize==1.4.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
inflection==0.5.1
|
inflection==0.5.1
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
jinja2==3.1.6
|
jinja2==3.1.6
|
||||||
# via
|
# via
|
||||||
# aiohttp-jinja2
|
# aiohttp-jinja2
|
||||||
@ -73,37 +73,37 @@ propcache==0.3.1
|
|||||||
# aiohttp
|
# aiohttp
|
||||||
# yarl
|
# yarl
|
||||||
pydeps==3.0.1
|
pydeps==3.0.1
|
||||||
# via ahriman (pyproject.toml:docs)
|
# via ahriman (../pyproject.toml:docs)
|
||||||
pyelftools==0.32
|
pyelftools==0.32
|
||||||
# via ahriman (pyproject.toml)
|
# via ahriman (../pyproject.toml)
|
||||||
pygments==2.19.1
|
pygments==2.19.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
# via botocore
|
# via botocore
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
# via
|
# via
|
||||||
# ahriman (pyproject.toml)
|
# ahriman (../pyproject.toml)
|
||||||
# sphinx
|
# sphinx
|
||||||
roman-numerals-py==3.1.0
|
roman-numerals-py==3.1.0
|
||||||
# via sphinx
|
# via sphinx
|
||||||
s3transfer==0.12.0
|
s3transfer==0.12.0
|
||||||
# via boto3
|
# via boto3
|
||||||
shtab==1.7.2
|
shtab==1.7.2
|
||||||
# via ahriman (pyproject.toml:docs)
|
# via ahriman (../pyproject.toml:docs)
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
# via python-dateutil
|
# via python-dateutil
|
||||||
snowballstemmer==3.0.0.1
|
snowballstemmer==3.0.0.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinx==8.2.3
|
sphinx==8.2.3
|
||||||
# via
|
# via
|
||||||
# ahriman (pyproject.toml:docs)
|
# ahriman (../pyproject.toml:docs)
|
||||||
# sphinx-argparse
|
# sphinx-argparse
|
||||||
# sphinx-rtd-theme
|
# sphinx-rtd-theme
|
||||||
# sphinxcontrib-jquery
|
# sphinxcontrib-jquery
|
||||||
sphinx-argparse==0.5.2
|
sphinx-argparse==0.5.2
|
||||||
# via ahriman (pyproject.toml:docs)
|
# via ahriman (../pyproject.toml:docs)
|
||||||
sphinx-rtd-theme==3.0.2
|
sphinx-rtd-theme==3.0.2
|
||||||
# via ahriman (pyproject.toml:docs)
|
# via ahriman (../pyproject.toml:docs)
|
||||||
sphinxcontrib-applehelp==2.0.0
|
sphinxcontrib-applehelp==2.0.0
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinxcontrib-devhelp==2.0.0
|
sphinxcontrib-devhelp==2.0.0
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
pkgbase='ahriman'
|
pkgbase='ahriman'
|
||||||
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
|
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
|
||||||
pkgver=2.18.3
|
pkgver=2.18.2
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="ArcH linux ReposItory MANager"
|
pkgdesc="ArcH linux ReposItory MANager"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
@ -24,13 +24,6 @@
|
|||||||
<datalist id="package-add-known-packages-dlist"></datalist>
|
<datalist id="package-add-known-packages-dlist"></datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
|
||||||
<label class="col-3 col-form-label"></label>
|
|
||||||
<div class="col-9">
|
|
||||||
<input id="package-add-refresh-input" type="checkbox" class="form-check-input" value="" checked>
|
|
||||||
<label for="package-add-refresh-input" class="form-check-label">refresh pacman databases</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button id="package-add-variable-button" type="button" class="form-control btn btn-light rounded" onclick="packageAddVariableInputCreate()"><i class="bi bi-plus"></i> add environment variable </button>
|
<button id="package-add-variable-button" type="button" class="form-control btn btn-light rounded" onclick="packageAddVariableInputCreate()"><i class="bi bi-plus"></i> add environment variable </button>
|
||||||
@ -57,8 +50,6 @@
|
|||||||
|
|
||||||
const packageAddVariablesDiv = document.getElementById("package-add-variables-div");
|
const packageAddVariablesDiv = document.getElementById("package-add-variables-div");
|
||||||
|
|
||||||
const packageAddRefreshInput = document.getElementById("package-add-refresh-input");
|
|
||||||
|
|
||||||
function packageAddVariableInputCreate() {
|
function packageAddVariableInputCreate() {
|
||||||
const variableInput = document.createElement("div");
|
const variableInput = document.createElement("div");
|
||||||
variableInput.classList.add("input-group");
|
variableInput.classList.add("input-group");
|
||||||
@ -112,13 +103,12 @@
|
|||||||
packages = packages ?? packageAddInput.value;
|
packages = packages ?? packageAddInput.value;
|
||||||
patches = patches ?? patchesParse();
|
patches = patches ?? patchesParse();
|
||||||
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
|
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
|
||||||
const parameters = Object.assign({}, {refresh: packageAddRefreshInput.checked}, patches);
|
|
||||||
|
|
||||||
if (packages) {
|
if (packages) {
|
||||||
bootstrap.Modal.getOrCreateInstance(packageAddModal).hide();
|
bootstrap.Modal.getOrCreateInstance(packageAddModal).hide();
|
||||||
const onSuccess = update => `Packages ${update} have been added`;
|
const onSuccess = update => `Packages ${update} have been added`;
|
||||||
const onFailure = error => `Package addition failed: ${error}`;
|
const onFailure = error => `Package addition failed: ${error}`;
|
||||||
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, parameters);
|
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, patches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.TH AHRIMAN "1" "2025\-06\-23" "ahriman 2.18.3" "ArcH linux ReposItory MANager"
|
.TH AHRIMAN "1" "2025\-06\-16" "ahriman" "Generated Python Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ahriman \- ArcH linux ReposItory MANager
|
ahriman
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B ahriman
|
.B ahriman
|
||||||
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {add,aur-search,check,clean,config,config-validate,copy,daemon,help,help-commands-unsafe,help-updates,help-version,init,key-import,package-add,package-changes,package-changes-remove,package-copy,package-remove,package-status,package-status-remove,package-status-update,package-update,patch-add,patch-list,patch-remove,patch-set-add,rebuild,remove,remove-unknown,repo-backup,repo-check,repo-clean,repo-config,repo-config-validate,repo-create-keyring,repo-create-mirrorlist,repo-daemon,repo-init,repo-rebuild,repo-remove-unknown,repo-report,repo-restore,repo-setup,repo-sign,repo-statistics,repo-status-update,repo-sync,repo-tree,repo-triggers,repo-update,report,run,search,service-clean,service-config,service-config-validate,service-key-import,service-repositories,service-run,service-setup,service-shell,service-tree-migrate,setup,shell,sign,status,status-update,sync,update,user-add,user-list,user-remove,version,web} ...
|
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {add,aur-search,check,clean,config,config-validate,copy,daemon,help,help-commands-unsafe,help-updates,help-version,init,key-import,package-add,package-changes,package-changes-remove,package-copy,package-remove,package-status,package-status-remove,package-status-update,package-update,patch-add,patch-list,patch-remove,patch-set-add,rebuild,remove,remove-unknown,repo-backup,repo-check,repo-clean,repo-config,repo-config-validate,repo-create-keyring,repo-create-mirrorlist,repo-daemon,repo-init,repo-rebuild,repo-remove-unknown,repo-report,repo-restore,repo-setup,repo-sign,repo-statistics,repo-status-update,repo-sync,repo-tree,repo-triggers,repo-update,report,run,search,service-clean,service-config,service-config-validate,service-key-import,service-repositories,service-run,service-setup,service-shell,service-tree-migrate,setup,shell,sign,status,status-update,sync,update,user-add,user-list,user-remove,version,web} ...
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
@ -58,23 +58,23 @@ web = [
|
|||||||
"aiohttp_cors",
|
"aiohttp_cors",
|
||||||
"aiohttp_jinja2",
|
"aiohttp_jinja2",
|
||||||
]
|
]
|
||||||
web-auth = [
|
web_api-docs = [
|
||||||
|
"ahriman[web]",
|
||||||
|
"aiohttp-apispec",
|
||||||
|
"setuptools", # required by aiohttp-apispec
|
||||||
|
]
|
||||||
|
web_auth = [
|
||||||
"ahriman[web]",
|
"ahriman[web]",
|
||||||
"aiohttp_session",
|
"aiohttp_session",
|
||||||
"aiohttp_security",
|
"aiohttp_security",
|
||||||
"cryptography",
|
"cryptography",
|
||||||
]
|
]
|
||||||
web-docs = [
|
web_metrics = [
|
||||||
"ahriman[web]",
|
|
||||||
"aiohttp-apispec",
|
|
||||||
"setuptools", # required by aiohttp-apispec
|
|
||||||
]
|
|
||||||
web-metrics = [
|
|
||||||
"ahriman[web]",
|
"ahriman[web]",
|
||||||
"aiohttp-openmetrics",
|
"aiohttp-openmetrics",
|
||||||
]
|
]
|
||||||
web-oauth2 = [
|
web_oauth2 = [
|
||||||
"ahriman[web-auth]",
|
"ahriman[web_auth]",
|
||||||
"aioauth-client",
|
"aioauth-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -17,4 +17,4 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
__version__ = "2.18.3"
|
__version__ = "2.18.2"
|
||||||
|
@ -133,18 +133,18 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
if not process_dependencies or not packages:
|
if not process_dependencies or not packages:
|
||||||
return packages
|
return packages
|
||||||
|
|
||||||
def missing_dependencies(sources: Iterable[Package]) -> dict[str, str | None]:
|
def missing_dependencies(source: Iterable[Package]) -> dict[str, str | None]:
|
||||||
# append list of known packages with packages which are in current sources
|
# append list of known packages with packages which are in current sources
|
||||||
satisfied_packages = known_packages | {
|
satisfied_packages = known_packages | {
|
||||||
single
|
single
|
||||||
for source in sources
|
for package in source
|
||||||
for single in source.packages_full
|
for single in package.packages_full
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dependency: source.packager
|
dependency: package.packager
|
||||||
for source in sources
|
for package in source
|
||||||
for dependency in source.depends_build
|
for dependency in package.depends_build
|
||||||
if dependency not in satisfied_packages
|
if dependency not in satisfied_packages
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
# there is local cache, load package from it
|
# there is local cache, load package from it
|
||||||
leaf = Package.from_build(source_dir, self.repository.architecture, packager)
|
leaf = Package.from_build(source_dir, self.repository.architecture, packager)
|
||||||
else:
|
else:
|
||||||
leaf = Package.from_aur(package_name, packager, include_provides=True)
|
leaf = Package.from_aur(package_name, packager)
|
||||||
portion[leaf.base] = leaf
|
portion[leaf.base] = leaf
|
||||||
|
|
||||||
# register package in the database
|
# register package in the database
|
||||||
|
@ -255,19 +255,3 @@ class Pacman(LazyLogging):
|
|||||||
result.update(trim_package(provides) for provides in package.provides)
|
result.update(trim_package(provides) for provides in package.provides)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def provided_by(self, package_name: str) -> Generator[Package, None, None]:
|
|
||||||
"""
|
|
||||||
search through databases and emit packages which provides the ``package_name``
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package_name(str): package name to search
|
|
||||||
|
|
||||||
Yields:
|
|
||||||
Package: list of packages which were returned by the query
|
|
||||||
"""
|
|
||||||
def is_package_provided(package: Package) -> bool:
|
|
||||||
return package_name in package.provides
|
|
||||||
|
|
||||||
for database in self.handle.get_syncdbs():
|
|
||||||
yield from filter(is_package_provided, database.search(package_name))
|
|
||||||
|
@ -97,17 +97,20 @@ class AUR(Remote):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: response parsed to package list
|
list[AURPackage]: response parsed to package list
|
||||||
|
|
||||||
Raises:
|
|
||||||
PackageInfoError: if multiple arguments are passed
|
|
||||||
"""
|
"""
|
||||||
if len(args) != 1:
|
query: list[tuple[str, str]] = [
|
||||||
raise PackageInfoError("AUR API requires exactly one argument to search")
|
("type", request_type),
|
||||||
|
("v", self.DEFAULT_RPC_VERSION),
|
||||||
|
]
|
||||||
|
|
||||||
url = f"{self.DEFAULT_RPC_URL}/v{self.DEFAULT_RPC_VERSION}/{request_type}/{args[0]}"
|
arg_query = "arg[]" if len(args) > 1 else "arg"
|
||||||
query = list(kwargs.items())
|
for arg in args:
|
||||||
|
query.append((arg_query, arg))
|
||||||
|
|
||||||
response = self.make_request("GET", url, params=query)
|
for key, value in kwargs.items():
|
||||||
|
query.append((key, value))
|
||||||
|
|
||||||
|
response = self.make_request("GET", self.DEFAULT_RPC_URL, params=query)
|
||||||
return self.parse_response(response.json())
|
return self.parse_response(response.json())
|
||||||
|
|
||||||
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
||||||
@ -130,36 +133,15 @@ class AUR(Remote):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise UnknownPackageError(package_name) from None
|
raise UnknownPackageError(package_name) from None
|
||||||
|
|
||||||
def package_provided_by(self, package_name: str, *, pacman: Pacman | None) -> list[AURPackage]:
|
def package_search(self, *keywords: str, pacman: Pacman | None) -> list[AURPackage]:
|
||||||
"""
|
|
||||||
get package list which provide the specified package name
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package_name(str): package name to search
|
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[AURPackage]: list of packages which match the criteria
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
package
|
|
||||||
# search api provides reduced models
|
|
||||||
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
|
||||||
# verity that found package actually provides it
|
|
||||||
if package_name in (package := self.package_info(stub.package_base, pacman=pacman)).provides
|
|
||||||
]
|
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*keywords(str): keywords to search
|
*keywords(str): keywords to search
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
search_by(str | None): search by keywords
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
"""
|
"""
|
||||||
search_by = search_by or "name-desc"
|
return self.aur_request("search", *keywords, by="name-desc")
|
||||||
return self.aur_request("search", *keywords, by=search_by)
|
|
||||||
|
@ -127,17 +127,15 @@ class Official(Remote):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise UnknownPackageError(package_name) from None
|
raise UnknownPackageError(package_name) from None
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
def package_search(self, *keywords: str, pacman: Pacman | None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*keywords(str): keywords to search
|
*keywords(str): keywords to search
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
search_by(str | None): search by keywords
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
"""
|
"""
|
||||||
search_by = search_by or "q"
|
return self.arch_request(*keywords, by="q")
|
||||||
return self.arch_request(*keywords, by=search_by)
|
|
||||||
|
@ -59,22 +59,3 @@ class OfficialSyncdb(Official):
|
|||||||
return next(AURPackage.from_pacman(package) for package in pacman.package(package_name))
|
return next(AURPackage.from_pacman(package) for package in pacman.package(package_name))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise UnknownPackageError(package_name) from None
|
raise UnknownPackageError(package_name) from None
|
||||||
|
|
||||||
def package_provided_by(self, package_name: str, *, pacman: Pacman | None) -> list[AURPackage]:
|
|
||||||
"""
|
|
||||||
get package list which provide the specified package name
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package_name(str): package name to search
|
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[AURPackage]: list of packages which match the criteria
|
|
||||||
"""
|
|
||||||
if pacman is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return [
|
|
||||||
AURPackage.from_pacman(package)
|
|
||||||
for package in pacman.provided_by(package_name)
|
|
||||||
]
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
from ahriman.core.exceptions import UnknownPackageError
|
|
||||||
from ahriman.core.http import SyncHttpClient
|
from ahriman.core.http import SyncHttpClient
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
|
|
||||||
@ -42,36 +41,22 @@ class Remote(SyncHttpClient):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def info(cls, package_name: str, *, pacman: Pacman | None = None, include_provides: bool = False) -> AURPackage:
|
def info(cls, package_name: str, *, pacman: Pacman | None = None) -> AURPackage:
|
||||||
"""
|
"""
|
||||||
get package info by its name. If ``include_provides`` is set to ``True``, then, in addition, this method
|
get package info by its name
|
||||||
will perform search by :attr:`ahriman.models.aur_package.AURPackage.provides` and return first package found.
|
|
||||||
Note, however, that in this case some implementation might not provide this method and search result will might
|
|
||||||
not be stable
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package_name(str): package name to search
|
package_name(str): package name to search
|
||||||
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
||||||
(Default value = None)
|
(Default value = None)
|
||||||
include_provides(bool, optional): search by provides if no exact match found (Default value = False)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AURPackage: package which match the package name
|
AURPackage: package which match the package name
|
||||||
|
|
||||||
Raises:
|
|
||||||
UnknownPackageError: if requested package not found
|
|
||||||
"""
|
"""
|
||||||
instance = cls()
|
return cls().package_info(package_name, pacman=pacman)
|
||||||
try:
|
|
||||||
return instance.package_info(package_name, pacman=pacman)
|
|
||||||
except UnknownPackageError:
|
|
||||||
if include_provides and (provided_by := instance.package_provided_by(package_name, pacman=pacman)):
|
|
||||||
return next(iter(provided_by))
|
|
||||||
raise
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def multisearch(cls, *keywords: str, pacman: Pacman | None = None,
|
def multisearch(cls, *keywords: str, pacman: Pacman | None = None) -> list[AURPackage]:
|
||||||
search_by: str | None = None) -> list[AURPackage]:
|
|
||||||
"""
|
"""
|
||||||
search in remote repository by using API with multiple words. This method is required in order to handle
|
search in remote repository by using API with multiple words. This method is required in order to handle
|
||||||
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
||||||
@ -80,7 +65,6 @@ class Remote(SyncHttpClient):
|
|||||||
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
||||||
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
||||||
(Default value = None)
|
(Default value = None)
|
||||||
search_by(str | None, optional): search by keywords (Default value = None)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages each of them matches all search terms
|
list[AURPackage]: list of packages each of them matches all search terms
|
||||||
@ -88,7 +72,7 @@ class Remote(SyncHttpClient):
|
|||||||
instance = cls()
|
instance = cls()
|
||||||
packages: dict[str, AURPackage] = {}
|
packages: dict[str, AURPackage] = {}
|
||||||
for term in filter(lambda word: len(word) >= 3, keywords):
|
for term in filter(lambda word: len(word) >= 3, keywords):
|
||||||
portion = instance.package_search(term, pacman=pacman, search_by=search_by)
|
portion = instance.search(term, pacman=pacman)
|
||||||
packages = {
|
packages = {
|
||||||
package.name: package # not mistake to group them by name
|
package.name: package # not mistake to group them by name
|
||||||
for package in portion
|
for package in portion
|
||||||
@ -130,7 +114,7 @@ class Remote(SyncHttpClient):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search(cls, *keywords: str, pacman: Pacman | None = None, search_by: str | None = None) -> list[AURPackage]:
|
def search(cls, *keywords: str, pacman: Pacman | None = None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
@ -138,12 +122,11 @@ class Remote(SyncHttpClient):
|
|||||||
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
||||||
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
||||||
(Default value = None)
|
(Default value = None)
|
||||||
search_by(str | None, optional): search by keywords (Default value = None)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
"""
|
"""
|
||||||
return cls().package_search(*keywords, pacman=pacman, search_by=search_by)
|
return cls().package_search(*keywords, pacman=pacman)
|
||||||
|
|
||||||
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
||||||
"""
|
"""
|
||||||
@ -161,28 +144,13 @@ class Remote(SyncHttpClient):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def package_provided_by(self, package_name: str, *, pacman: Pacman | None) -> list[AURPackage]:
|
def package_search(self, *keywords: str, pacman: Pacman | None) -> list[AURPackage]:
|
||||||
"""
|
|
||||||
get package list which provide the specified package name
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package_name(str): package name to search
|
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[AURPackage]: list of packages which match the criteria
|
|
||||||
"""
|
|
||||||
del package_name, pacman
|
|
||||||
return []
|
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*keywords(str): keywords to search
|
*keywords(str): keywords to search
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
search_by(str | None): search by keywords
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
|
@ -210,17 +210,6 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
raise InitializeError("Configuration path and/or repository id are not set")
|
raise InitializeError("Configuration path and/or repository id are not set")
|
||||||
return self.path, self.repository_id
|
return self.path, self.repository_id
|
||||||
|
|
||||||
def copy_from(self, configuration: Self) -> None:
|
|
||||||
"""
|
|
||||||
copy values from another instance overriding existing
|
|
||||||
|
|
||||||
Args:
|
|
||||||
configuration(Self): configuration instance to merge from
|
|
||||||
"""
|
|
||||||
for section in configuration.sections():
|
|
||||||
for key, value in configuration.items(section):
|
|
||||||
self.set_option(section, key, value)
|
|
||||||
|
|
||||||
def dump(self) -> dict[str, dict[str, str]]:
|
def dump(self) -> dict[str, dict[str, str]]:
|
||||||
"""
|
"""
|
||||||
dump configuration to dictionary
|
dump configuration to dictionary
|
||||||
@ -231,7 +220,6 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
return {
|
return {
|
||||||
section: dict(self.items(section))
|
section: dict(self.items(section))
|
||||||
for section in self.sections()
|
for section in self.sections()
|
||||||
if self[section]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# pylint and mypy are too stupid to find these methods
|
# pylint and mypy are too stupid to find these methods
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import requests
|
import requests
|
||||||
import sys
|
|
||||||
|
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, IO, Literal
|
from typing import Any, IO, Literal
|
||||||
@ -71,10 +70,7 @@ class SyncHttpClient(LazyLogging):
|
|||||||
request.Session: created session object
|
request.Session: created session object
|
||||||
"""
|
"""
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
python_version = ".".join(map(str, sys.version_info[:3])) # just major.minor.patch
|
session.headers["User-Agent"] = f"ahriman/{__version__}"
|
||||||
session.headers["User-Agent"] = f"ahriman/{__version__}" \
|
|
||||||
f"{requests.utils.default_user_agent()}" \
|
|
||||||
f"python/{python_version}"
|
|
||||||
|
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ class Leaf:
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
dependencies(set[str]): list of package dependencies
|
dependencies(set[str]): list of package dependencies
|
||||||
items(list[str]): list of packages in this leaf including provides
|
|
||||||
package(Package): leaf package properties
|
package(Package): leaf package properties
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -43,9 +42,17 @@ class Leaf:
|
|||||||
package(Package): package properties
|
package(Package): package properties
|
||||||
"""
|
"""
|
||||||
self.package = package
|
self.package = package
|
||||||
# store frequently used properties
|
|
||||||
self.dependencies = package.depends_build
|
self.dependencies = package.depends_build
|
||||||
self.items = self.package.packages_full
|
|
||||||
|
@property
|
||||||
|
def items(self) -> Iterable[str]:
|
||||||
|
"""
|
||||||
|
extract all packages from the leaf
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Iterable[str]: packages containing in this leaf
|
||||||
|
"""
|
||||||
|
return self.package.packages.keys()
|
||||||
|
|
||||||
def is_dependency(self, packages: Iterable[Leaf]) -> bool:
|
def is_dependency(self, packages: Iterable[Leaf]) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -136,8 +136,7 @@ def check_output(*args: str, exception: Exception | Callable[[int, list[str], st
|
|||||||
} | environment
|
} | environment
|
||||||
|
|
||||||
with subprocess.Popen(args, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
with subprocess.Popen(args, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
user=user, env=full_environment, text=True, encoding="utf8", errors="backslashreplace",
|
user=user, env=full_environment, text=True, encoding="utf8", bufsize=1) as process:
|
||||||
bufsize=1) as process:
|
|
||||||
if input_data is not None:
|
if input_data is not None:
|
||||||
input_channel = get_io(process, "stdin")
|
input_channel = get_io(process, "stdin")
|
||||||
input_channel.write(input_data)
|
input_channel.write(input_data)
|
||||||
|
@ -213,19 +213,18 @@ class Package(LazyLogging):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_aur(cls, name: str, packager: str | None = None, *, include_provides: bool = False) -> Self:
|
def from_aur(cls, name: str, packager: str | None = None) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from AUR page
|
construct package properties from AUR page
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name(str): package name (either base or normal name)
|
name(str): package name (either base or normal name)
|
||||||
packager(str | None, optional): packager to be used for this build (Default value = None)
|
packager(str | None, optional): packager to be used for this build (Default value = None)
|
||||||
include_provides(bool, optional): search by provides if no exact match found (Default value = False)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Self: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
package = AUR.info(name, include_provides=include_provides)
|
package = AUR.info(name)
|
||||||
|
|
||||||
remote = RemoteSource(
|
remote = RemoteSource(
|
||||||
source=PackageSource.AUR,
|
source=PackageSource.AUR,
|
||||||
@ -311,8 +310,7 @@ class Package(LazyLogging):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_official(cls, name: str, pacman: Pacman, packager: str | None = None, *, use_syncdb: bool = True,
|
def from_official(cls, name: str, pacman: Pacman, packager: str | None = None, *, use_syncdb: bool = True) -> Self:
|
||||||
include_provides: bool = False) -> Self:
|
|
||||||
"""
|
"""
|
||||||
construct package properties from official repository page
|
construct package properties from official repository page
|
||||||
|
|
||||||
@ -321,13 +319,11 @@ class Package(LazyLogging):
|
|||||||
pacman(Pacman): alpm wrapper instance
|
pacman(Pacman): alpm wrapper instance
|
||||||
packager(str | None, optional): packager to be used for this build (Default value = None)
|
packager(str | None, optional): packager to be used for this build (Default value = None)
|
||||||
use_syncdb(bool, optional): use pacman databases instead of official repositories RPC (Default value = True)
|
use_syncdb(bool, optional): use pacman databases instead of official repositories RPC (Default value = True)
|
||||||
include_provides(bool, optional): search by provides if no exact match found (Default value = False)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Self: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
impl = OfficialSyncdb if use_syncdb else Official
|
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name)
|
||||||
package = impl.info(name, pacman=pacman, include_provides=include_provides)
|
|
||||||
|
|
||||||
remote = RemoteSource(
|
remote = RemoteSource(
|
||||||
source=PackageSource.Repository,
|
source=PackageSource.Repository,
|
||||||
|
@ -72,8 +72,8 @@ def setup_routes(application: Application, configuration: Configuration) -> None
|
|||||||
application(Application): web application instance
|
application(Application): web application instance
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
"""
|
"""
|
||||||
application.router.add_static("/static", configuration.getpath("web", "static_path"),
|
application.router.add_static("/static", configuration.getpath("web", "static_path"), name="_static",
|
||||||
name="_static", follow_symlinks=True)
|
follow_symlinks=True)
|
||||||
|
|
||||||
for route, view in _dynamic_routes(configuration):
|
for route, view in _dynamic_routes(configuration):
|
||||||
application.router.add_view(route, view, name=_identifier(route))
|
application.router.add_view(route, view, name=_identifier(route))
|
||||||
|
@ -97,7 +97,6 @@ class LoginView(BaseView):
|
|||||||
login user to service. The authentication session will be passed in ``Set-Cookie`` header.
|
login user to service. The authentication session will be passed in ``Set-Cookie`` header.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
HTTPBadRequest: if bad data is supplied
|
|
||||||
HTTPFound: on success response
|
HTTPFound: on success response
|
||||||
HTTPUnauthorized: if case of authorization error
|
HTTPUnauthorized: if case of authorization error
|
||||||
"""
|
"""
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2024 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
from pathlib import Path
|
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from typing import Any
|
|
||||||
from unittest.mock import MagicMock, call as MockCall
|
from unittest.mock import MagicMock, call as MockCall
|
||||||
|
|
||||||
from ahriman.application.application import Application
|
from ahriman.application.application import Application
|
||||||
@ -75,10 +73,6 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p
|
|||||||
mock.packages_full = [package_base]
|
mock.packages_full = [package_base]
|
||||||
return mock
|
return mock
|
||||||
|
|
||||||
def get_package(name: str | Path, *args: Any, **kwargs: Any) -> Package:
|
|
||||||
name = name if isinstance(name, str) else name.name
|
|
||||||
return packages[name]
|
|
||||||
|
|
||||||
package_python_schedule.packages = {
|
package_python_schedule.packages = {
|
||||||
package_python_schedule.base: package_python_schedule.packages[package_python_schedule.base]
|
package_python_schedule.base: package_python_schedule.packages[package_python_schedule.base]
|
||||||
}
|
}
|
||||||
@ -93,8 +87,10 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p
|
|||||||
}
|
}
|
||||||
|
|
||||||
mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=lambda p: p.name == "python")
|
mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=lambda p: p.name == "python")
|
||||||
package_aur_mock = mocker.patch("ahriman.models.package.Package.from_aur", side_effect=get_package)
|
package_aur_mock = mocker.patch("ahriman.models.package.Package.from_aur",
|
||||||
package_local_mock = mocker.patch("ahriman.models.package.Package.from_build", side_effect=get_package)
|
side_effect=lambda *args: packages[args[0]])
|
||||||
|
package_local_mock = mocker.patch("ahriman.models.package.Package.from_build",
|
||||||
|
side_effect=lambda *args: packages[args[0].name])
|
||||||
packages_mock = mocker.patch("ahriman.application.application.Application._known_packages",
|
packages_mock = mocker.patch("ahriman.application.application.Application._known_packages",
|
||||||
return_value={"devtools", "python-build", "python-pytest"})
|
return_value={"devtools", "python-build", "python-pytest"})
|
||||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_unknown")
|
status_client_mock = mocker.patch("ahriman.core.status.Client.set_unknown")
|
||||||
@ -102,8 +98,8 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p
|
|||||||
result = application.with_dependencies([package_ahriman], process_dependencies=True)
|
result = application.with_dependencies([package_ahriman], process_dependencies=True)
|
||||||
assert {package.base: package for package in result} == packages
|
assert {package.base: package for package in result} == packages
|
||||||
package_aur_mock.assert_has_calls([
|
package_aur_mock.assert_has_calls([
|
||||||
MockCall(package_python_schedule.base, package_ahriman.packager, include_provides=True),
|
MockCall(package_python_schedule.base, package_ahriman.packager),
|
||||||
MockCall("python-installer", package_ahriman.packager, include_provides=True),
|
MockCall("python-installer", package_ahriman.packager),
|
||||||
], any_order=True)
|
], any_order=True)
|
||||||
package_local_mock.assert_has_calls([
|
package_local_mock.assert_has_calls([
|
||||||
MockCall(application.repository.paths.cache_for("python"), "x86_64", package_ahriman.packager),
|
MockCall(application.repository.paths.cache_for("python"), "x86_64", package_ahriman.packager),
|
||||||
|
@ -144,7 +144,6 @@ def test_repositories_extract(args: argparse.Namespace, configuration: Configura
|
|||||||
args.architecture = "arch"
|
args.architecture = "arch"
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
args.repository = "repo"
|
args.repository = "repo"
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||||
|
|
||||||
@ -160,7 +159,6 @@ def test_repositories_extract_repository(args: argparse.Namespace, configuration
|
|||||||
"""
|
"""
|
||||||
args.architecture = "arch"
|
args.architecture = "arch"
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
||||||
return_value={"repo"})
|
return_value={"repo"})
|
||||||
@ -177,7 +175,6 @@ def test_repositories_extract_repository_legacy(args: argparse.Namespace, config
|
|||||||
"""
|
"""
|
||||||
args.architecture = "arch"
|
args.architecture = "arch"
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
||||||
return_value=set())
|
return_value=set())
|
||||||
@ -194,7 +191,6 @@ def test_repositories_extract_architecture(args: argparse.Namespace, configurati
|
|||||||
"""
|
"""
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
args.repository = "repo"
|
args.repository = "repo"
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures",
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures",
|
||||||
return_value={"arch"})
|
return_value={"arch"})
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||||
@ -211,7 +207,6 @@ def test_repositories_extract_empty(args: argparse.Namespace, configuration: Con
|
|||||||
"""
|
"""
|
||||||
args.command = "config"
|
args.command = "config"
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set())
|
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set())
|
||||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set())
|
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set())
|
||||||
|
|
||||||
@ -226,7 +221,6 @@ def test_repositories_extract_systemd(args: argparse.Namespace, configuration: C
|
|||||||
"""
|
"""
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
args.repository_id = "i686/some/repo/name"
|
args.repository_id = "i686/some/repo/name"
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||||
|
|
||||||
@ -242,7 +236,6 @@ def test_repositories_extract_systemd_with_dash(args: argparse.Namespace, config
|
|||||||
"""
|
"""
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
args.repository_id = "i686-some-repo-name"
|
args.repository_id = "i686-some-repo-name"
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
|
||||||
|
|
||||||
@ -258,7 +251,6 @@ def test_repositories_extract_systemd_legacy(args: argparse.Namespace, configura
|
|||||||
"""
|
"""
|
||||||
args.configuration = configuration.path
|
args.configuration = configuration.path
|
||||||
args.repository_id = "i686"
|
args.repository_id = "i686"
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
|
||||||
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
|
||||||
return_value=set())
|
return_value=set())
|
||||||
|
@ -6,7 +6,6 @@ from pytest_mock import MockerFixture
|
|||||||
from ahriman.application.application import Application
|
from ahriman.application.application import Application
|
||||||
from ahriman.application.handlers.copy import Copy
|
from ahriman.application.handlers.copy import Copy
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.database import SQLite
|
|
||||||
from ahriman.core.repository import Repository
|
from ahriman.core.repository import Repository
|
||||||
from ahriman.models.build_status import BuildStatusEnum
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
@ -31,12 +30,11 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
|||||||
|
|
||||||
|
|
||||||
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
|
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must run command
|
must run command
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
|
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
|
||||||
application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
|
application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
|
||||||
@ -53,13 +51,12 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
|
|||||||
|
|
||||||
|
|
||||||
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
|
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must run command and remove packages afterward
|
must run command and remove packages afterward
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.remove = True
|
args.remove = True
|
||||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
|
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
|
||||||
mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
|
mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
|
||||||
@ -72,14 +69,12 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
|
|||||||
|
|
||||||
|
|
||||||
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
database: SQLite, mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must raise ExitCode exception on empty result
|
must raise ExitCode exception on empty result
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.exit_code = True
|
args.exit_code = True
|
||||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
|
||||||
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
|
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
|
||||||
mocker.patch("ahriman.application.application.Application.update")
|
mocker.patch("ahriman.application.application.Application.update")
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
|
@ -9,7 +9,6 @@ from urllib.parse import quote_plus as url_encode
|
|||||||
|
|
||||||
from ahriman.application.handlers.setup import Setup
|
from ahriman.application.handlers.setup import Setup
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.database import SQLite
|
|
||||||
from ahriman.core.exceptions import MissingArchitectureError
|
from ahriman.core.exceptions import MissingArchitectureError
|
||||||
from ahriman.core.repository import Repository
|
from ahriman.core.repository import Repository
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
@ -45,12 +44,11 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
|||||||
|
|
||||||
|
|
||||||
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
database: SQLite, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must run command
|
must run command
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
|
ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
|
||||||
devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
|
devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
|
||||||
@ -90,13 +88,12 @@ def test_run_no_architecture_or_repository(configuration: Configuration) -> None
|
|||||||
|
|
||||||
|
|
||||||
def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
database: SQLite, mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must run command with server specified
|
must run command with server specified
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.server = "server"
|
args.server = "server"
|
||||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
|
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
|
||||||
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
|
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
|
||||||
|
@ -51,8 +51,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, database: S
|
|||||||
update_mock.assert_called_once_with(user)
|
update_mock.assert_called_once_with(user)
|
||||||
|
|
||||||
|
|
||||||
def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, database: SQLite,
|
def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
"""
|
||||||
must process users with empty password salt
|
must process users with empty password salt
|
||||||
"""
|
"""
|
||||||
@ -60,7 +59,6 @@ def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration,
|
|||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
user = User(username=args.username, password=args.password, access=args.role,
|
user = User(username=args.username, password=args.password, access=args.role,
|
||||||
packager_id=args.packager, key=args.key)
|
packager_id=args.packager, key=args.key)
|
||||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
|
||||||
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
|
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
|
||||||
create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
|
create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
|
||||||
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
|
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
|
||||||
|
@ -1575,7 +1575,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
|||||||
args.command = ""
|
args.command = ""
|
||||||
args.handler = Handler
|
args.handler = Handler
|
||||||
|
|
||||||
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
|
|
||||||
mocker.patch("argparse.ArgumentParser.parse_args", return_value=args)
|
mocker.patch("argparse.ArgumentParser.parse_args", return_value=args)
|
||||||
|
|
||||||
assert ahriman.run() == 1
|
assert ahriman.run() == 1
|
||||||
|
@ -249,25 +249,19 @@ def auth(configuration: Configuration) -> Auth:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def configuration(repository_id: RepositoryId, tmp_path: Path, resource_path_root: Path) -> Configuration:
|
def configuration(repository_id: RepositoryId, resource_path_root: Path) -> Configuration:
|
||||||
"""
|
"""
|
||||||
configuration fixture
|
configuration fixture
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
repository_id(RepositoryId): repository identifier fixture
|
repository_id(RepositoryId): repository identifier fixture
|
||||||
tmp_path(Path): temporary path used by the fixture as root
|
|
||||||
resource_path_root(Path): resource path root directory
|
resource_path_root(Path): resource path root directory
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Configuration: configuration test instance
|
Configuration: configuration test instance
|
||||||
"""
|
"""
|
||||||
path = resource_path_root / "core" / "ahriman.ini"
|
path = resource_path_root / "core" / "ahriman.ini"
|
||||||
|
return Configuration.from_path(path, repository_id)
|
||||||
instance = Configuration.from_path(path, repository_id)
|
|
||||||
instance.set_option("repository", "root", str(tmp_path))
|
|
||||||
instance.set_option("settings", "database", str(tmp_path / "ahriman.db"))
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -281,7 +275,9 @@ def database(configuration: Configuration) -> SQLite:
|
|||||||
Returns:
|
Returns:
|
||||||
SQLite: database test instance
|
SQLite: database test instance
|
||||||
"""
|
"""
|
||||||
return SQLite.load(configuration)
|
database = SQLite.load(configuration)
|
||||||
|
yield database
|
||||||
|
database.path.unlink()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -4,7 +4,7 @@ import requests
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest.mock import MagicMock, call as MockCall
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from ahriman.core.alpm.remote import AUR
|
from ahriman.core.alpm.remote import AUR
|
||||||
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
|
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
|
||||||
@ -76,18 +76,24 @@ def test_aur_request(aur: AUR, aur_package_ahriman: AURPackage,
|
|||||||
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
|
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
|
||||||
|
|
||||||
assert aur.aur_request("info", "ahriman") == [aur_package_ahriman]
|
assert aur.aur_request("info", "ahriman") == [aur_package_ahriman]
|
||||||
request_mock.assert_called_once_with("GET", "https://aur.archlinux.org/rpc/v5/info/ahriman", params=[])
|
request_mock.assert_called_once_with(
|
||||||
|
"GET", "https://aur.archlinux.org/rpc",
|
||||||
|
params=[("type", "info"), ("v", "5"), ("arg", "ahriman")])
|
||||||
|
|
||||||
|
|
||||||
def test_aur_request_multi_arg(aur: AUR) -> None:
|
def test_aur_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
|
||||||
|
mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||||
"""
|
"""
|
||||||
must raise PackageInfoError if invalid amount of arguments supplied
|
must perform request to AUR with multiple args
|
||||||
"""
|
"""
|
||||||
with pytest.raises(PackageInfoError):
|
response_mock = MagicMock()
|
||||||
aur.aur_request("search", "ahriman", "is", "cool")
|
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
|
||||||
|
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
|
||||||
|
|
||||||
with pytest.raises(PackageInfoError):
|
assert aur.aur_request("search", "ahriman", "is", "cool") == [aur_package_ahriman]
|
||||||
aur.aur_request("search")
|
request_mock.assert_called_once_with(
|
||||||
|
"GET", "https://aur.archlinux.org/rpc",
|
||||||
|
params=[("type", "search"), ("v", "5"), ("arg[]", "ahriman"), ("arg[]", "is"), ("arg[]", "cool")])
|
||||||
|
|
||||||
|
|
||||||
def test_aur_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
|
def test_aur_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
|
||||||
@ -100,8 +106,9 @@ def test_aur_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
|
|||||||
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
|
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
|
||||||
|
|
||||||
assert aur.aur_request("search", "ahriman", by="name") == [aur_package_ahriman]
|
assert aur.aur_request("search", "ahriman", by="name") == [aur_package_ahriman]
|
||||||
request_mock.assert_called_once_with("GET", "https://aur.archlinux.org/rpc/v5/search/ahriman",
|
request_mock.assert_called_once_with(
|
||||||
params=[("by", "name")])
|
"GET", "https://aur.archlinux.org/rpc",
|
||||||
|
params=[("type", "search"), ("v", "5"), ("arg", "ahriman"), ("by", "name")])
|
||||||
|
|
||||||
|
|
||||||
def test_aur_request_failed(aur: AUR, mocker: MockerFixture) -> None:
|
def test_aur_request_failed(aur: AUR, mocker: MockerFixture) -> None:
|
||||||
@ -132,46 +139,17 @@ def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerF
|
|||||||
|
|
||||||
def test_package_info_not_found(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
def test_package_info_not_found(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must raise UnknownPackageError in case if no package was found
|
must raise UnknownPackage exception in case if no package was found
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[])
|
mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[])
|
||||||
with pytest.raises(UnknownPackageError, match=aur_package_ahriman.name):
|
with pytest.raises(UnknownPackageError, match=aur_package_ahriman.name):
|
||||||
assert aur.package_info(aur_package_ahriman.name, pacman=None)
|
assert aur.package_info(aur_package_ahriman.name, pacman=None)
|
||||||
|
|
||||||
|
|
||||||
def test_package_provided_by(aur: AUR, aur_package_ahriman: AURPackage, aur_package_akonadi: AURPackage,
|
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must search for packages which provide required one
|
|
||||||
"""
|
|
||||||
aur_package_ahriman.provides.append(aur_package_ahriman.name)
|
|
||||||
search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.package_search", return_value=[
|
|
||||||
aur_package_ahriman, aur_package_akonadi
|
|
||||||
])
|
|
||||||
info_mock = mocker.patch("ahriman.core.alpm.remote.AUR.package_info", side_effect=[
|
|
||||||
aur_package_ahriman, aur_package_akonadi
|
|
||||||
])
|
|
||||||
|
|
||||||
assert aur.package_provided_by(aur_package_ahriman.name, pacman=None) == [aur_package_ahriman]
|
|
||||||
search_mock.assert_called_once_with(aur_package_ahriman.name, pacman=None, search_by="provides")
|
|
||||||
info_mock.assert_has_calls([
|
|
||||||
MockCall(aur_package_ahriman.name, pacman=None), MockCall(aur_package_akonadi.name, pacman=None)
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must make request for search
|
must make request for search
|
||||||
"""
|
"""
|
||||||
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[aur_package_ahriman])
|
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[aur_package_ahriman])
|
||||||
assert aur.package_search(aur_package_ahriman.name, pacman=None, search_by=None) == [aur_package_ahriman]
|
assert aur.package_search(aur_package_ahriman.name, pacman=None) == [aur_package_ahriman]
|
||||||
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name-desc")
|
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name-desc")
|
||||||
|
|
||||||
|
|
||||||
def test_package_search_provides(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must make request for search with custom field
|
|
||||||
"""
|
|
||||||
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.aur_request")
|
|
||||||
aur.package_search(aur_package_ahriman.name, pacman=None, search_by="provides")
|
|
||||||
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="provides")
|
|
||||||
|
@ -106,7 +106,7 @@ def test_package_info(official: Official, aur_package_akonadi: AURPackage, mocke
|
|||||||
|
|
||||||
def test_package_info_not_found(official: Official, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
def test_package_info_not_found(official: Official, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must raise UnknownPackageError in case if no package was found
|
must raise UnknownPackage exception in case if no package was found
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.alpm.remote.Official.arch_request", return_value=[])
|
mocker.patch("ahriman.core.alpm.remote.Official.arch_request", return_value=[])
|
||||||
with pytest.raises(UnknownPackageError, match=aur_package_ahriman.name):
|
with pytest.raises(UnknownPackageError, match=aur_package_ahriman.name):
|
||||||
@ -119,16 +119,5 @@ def test_package_search(official: Official, aur_package_akonadi: AURPackage, moc
|
|||||||
"""
|
"""
|
||||||
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.arch_request",
|
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.arch_request",
|
||||||
return_value=[aur_package_akonadi])
|
return_value=[aur_package_akonadi])
|
||||||
assert official.package_search(aur_package_akonadi.name, pacman=None, search_by=None) == [
|
assert official.package_search(aur_package_akonadi.name, pacman=None) == [aur_package_akonadi]
|
||||||
aur_package_akonadi,
|
|
||||||
]
|
|
||||||
request_mock.assert_called_once_with(aur_package_akonadi.name, by="q")
|
request_mock.assert_called_once_with(aur_package_akonadi.name, by="q")
|
||||||
|
|
||||||
|
|
||||||
def test_package_search_name(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must make request for search with custom field
|
|
||||||
"""
|
|
||||||
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.arch_request")
|
|
||||||
official.package_search(aur_package_akonadi.name, pacman=None, search_by="name")
|
|
||||||
request_mock.assert_called_once_with(aur_package_akonadi.name, by="name")
|
|
||||||
|
@ -16,14 +16,18 @@ def test_package_info(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURP
|
|||||||
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
|
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
|
||||||
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
|
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
|
||||||
|
|
||||||
assert official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman) == aur_package_akonadi
|
package = official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
|
||||||
get_mock.assert_called_once_with(aur_package_akonadi.name)
|
get_mock.assert_called_once_with(aur_package_akonadi.name)
|
||||||
|
assert package == aur_package_akonadi
|
||||||
|
|
||||||
|
|
||||||
def test_package_info_no_pacman(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage) -> None:
|
def test_package_info_no_pacman(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must raise UnknownPackageError if no pacman set
|
must raise UnknownPackageError if no pacman set
|
||||||
"""
|
"""
|
||||||
|
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
|
||||||
|
|
||||||
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
|
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
|
||||||
official_syncdb.package_info(aur_package_akonadi.name, pacman=None)
|
official_syncdb.package_info(aur_package_akonadi.name, pacman=None)
|
||||||
|
|
||||||
@ -36,22 +40,3 @@ def test_package_info_not_found(official_syncdb: OfficialSyncdb, aur_package_ako
|
|||||||
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[])
|
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[])
|
||||||
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
|
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
|
||||||
assert official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
|
assert official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
|
||||||
|
|
||||||
|
|
||||||
def test_package_provided_by(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage, pacman: Pacman,
|
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must search by provides in database
|
|
||||||
"""
|
|
||||||
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
|
|
||||||
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.provided_by", return_value=[aur_package_akonadi])
|
|
||||||
|
|
||||||
assert official_syncdb.package_provided_by(aur_package_akonadi.name, pacman=pacman) == [aur_package_akonadi]
|
|
||||||
get_mock.assert_called_once_with(aur_package_akonadi.name)
|
|
||||||
|
|
||||||
|
|
||||||
def test_package_provided_by_no_pacman(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage) -> None:
|
|
||||||
"""
|
|
||||||
must return empty list if no pacman set
|
|
||||||
"""
|
|
||||||
assert official_syncdb.package_provided_by(aur_package_akonadi.name, pacman=None) == []
|
|
||||||
|
@ -5,53 +5,16 @@ from unittest.mock import call as MockCall
|
|||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
from ahriman.core.alpm.remote import Remote
|
from ahriman.core.alpm.remote import Remote
|
||||||
from ahriman.core.exceptions import UnknownPackageError
|
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
|
|
||||||
|
|
||||||
def test_info(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
def test_info(pacman: Pacman, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must call info method
|
must call info method
|
||||||
"""
|
"""
|
||||||
info_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_info", return_value=aur_package_ahriman)
|
info_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_info")
|
||||||
assert Remote.info(aur_package_ahriman.name, pacman=pacman) == aur_package_ahriman
|
Remote.info("ahriman", pacman=pacman)
|
||||||
info_mock.assert_called_once_with(aur_package_ahriman.name, pacman=pacman)
|
info_mock.assert_called_once_with("ahriman", pacman=pacman)
|
||||||
|
|
||||||
|
|
||||||
def test_info_not_found(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must raise UnknownPackageError if no package found and search by provides is disabled
|
|
||||||
"""
|
|
||||||
mocker.patch("ahriman.core.alpm.remote.Remote.package_info",
|
|
||||||
side_effect=UnknownPackageError(aur_package_ahriman.name))
|
|
||||||
with pytest.raises(UnknownPackageError):
|
|
||||||
Remote.info(aur_package_ahriman.name, pacman=pacman)
|
|
||||||
|
|
||||||
|
|
||||||
def test_info_include_provides(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must perform search through provides list is set
|
|
||||||
"""
|
|
||||||
mocker.patch("ahriman.core.alpm.remote.Remote.package_info",
|
|
||||||
side_effect=UnknownPackageError(aur_package_ahriman.name))
|
|
||||||
provided_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_provided_by",
|
|
||||||
return_value=[aur_package_ahriman])
|
|
||||||
|
|
||||||
assert Remote.info(aur_package_ahriman.name, pacman=pacman, include_provides=True) == aur_package_ahriman
|
|
||||||
provided_mock.assert_called_once_with(aur_package_ahriman.name, pacman=pacman)
|
|
||||||
|
|
||||||
|
|
||||||
def test_info_include_provides_not_found(aur_package_ahriman: AURPackage, pacman: Pacman,
|
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must raise UnknownPackageError if no package found and search by provides returns empty list
|
|
||||||
"""
|
|
||||||
mocker.patch("ahriman.core.alpm.remote.Remote.package_info",
|
|
||||||
side_effect=UnknownPackageError(aur_package_ahriman.name))
|
|
||||||
mocker.patch("ahriman.core.alpm.remote.Remote.package_provided_by", return_value=[])
|
|
||||||
|
|
||||||
with pytest.raises(UnknownPackageError):
|
|
||||||
Remote.info("ahriman", pacman=pacman, include_provides=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_multisearch(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
def test_multisearch(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
|
||||||
@ -59,13 +22,10 @@ def test_multisearch(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: Mo
|
|||||||
must search in AUR with multiple words
|
must search in AUR with multiple words
|
||||||
"""
|
"""
|
||||||
terms = ["ahriman", "is", "cool"]
|
terms = ["ahriman", "is", "cool"]
|
||||||
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search", return_value=[aur_package_ahriman])
|
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.search", return_value=[aur_package_ahriman])
|
||||||
|
|
||||||
assert Remote.multisearch(*terms, pacman=pacman, search_by="name") == [aur_package_ahriman]
|
assert Remote.multisearch(*terms, pacman=pacman) == [aur_package_ahriman]
|
||||||
search_mock.assert_has_calls([
|
search_mock.assert_has_calls([MockCall("ahriman", pacman=pacman), MockCall("cool", pacman=pacman)])
|
||||||
MockCall("ahriman", pacman=pacman, search_by="name"),
|
|
||||||
MockCall("cool", pacman=pacman, search_by="name"),
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def test_multisearch_empty(pacman: Pacman, mocker: MockerFixture) -> None:
|
def test_multisearch_empty(pacman: Pacman, mocker: MockerFixture) -> None:
|
||||||
@ -73,7 +33,7 @@ def test_multisearch_empty(pacman: Pacman, mocker: MockerFixture) -> None:
|
|||||||
must return empty list if no long terms supplied
|
must return empty list if no long terms supplied
|
||||||
"""
|
"""
|
||||||
terms = ["it", "is"]
|
terms = ["it", "is"]
|
||||||
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search")
|
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.search")
|
||||||
|
|
||||||
assert Remote.multisearch(*terms, pacman=pacman) == []
|
assert Remote.multisearch(*terms, pacman=pacman) == []
|
||||||
search_mock.assert_not_called()
|
search_mock.assert_not_called()
|
||||||
@ -83,9 +43,9 @@ def test_multisearch_single(aur_package_ahriman: AURPackage, pacman: Pacman, moc
|
|||||||
"""
|
"""
|
||||||
must search in AUR with one word
|
must search in AUR with one word
|
||||||
"""
|
"""
|
||||||
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search", return_value=[aur_package_ahriman])
|
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.search", return_value=[aur_package_ahriman])
|
||||||
assert Remote.multisearch("ahriman", pacman=pacman) == [aur_package_ahriman]
|
assert Remote.multisearch("ahriman", pacman=pacman) == [aur_package_ahriman]
|
||||||
search_mock.assert_called_once_with("ahriman", pacman=pacman, search_by=None)
|
search_mock.assert_called_once_with("ahriman", pacman=pacman)
|
||||||
|
|
||||||
|
|
||||||
def test_remote_git_url(remote: Remote) -> None:
|
def test_remote_git_url(remote: Remote) -> None:
|
||||||
@ -109,8 +69,8 @@ def test_search(pacman: Pacman, mocker: MockerFixture) -> None:
|
|||||||
must call search method
|
must call search method
|
||||||
"""
|
"""
|
||||||
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search")
|
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search")
|
||||||
Remote.search("ahriman", pacman=pacman, search_by="name")
|
Remote.search("ahriman", pacman=pacman)
|
||||||
search_mock.assert_called_once_with("ahriman", pacman=pacman, search_by="name")
|
search_mock.assert_called_once_with("ahriman", pacman=pacman)
|
||||||
|
|
||||||
|
|
||||||
def test_package_info(remote: Remote, pacman: Pacman) -> None:
|
def test_package_info(remote: Remote, pacman: Pacman) -> None:
|
||||||
@ -121,16 +81,9 @@ def test_package_info(remote: Remote, pacman: Pacman) -> None:
|
|||||||
remote.package_info("package", pacman=pacman)
|
remote.package_info("package", pacman=pacman)
|
||||||
|
|
||||||
|
|
||||||
def test_package_provided_by(remote: Remote, pacman: Pacman) -> None:
|
|
||||||
"""
|
|
||||||
must return empty list for provides method
|
|
||||||
"""
|
|
||||||
assert remote.package_provided_by("package", pacman=pacman) == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_package_search(remote: Remote, pacman: Pacman) -> None:
|
def test_package_search(remote: Remote, pacman: Pacman) -> None:
|
||||||
"""
|
"""
|
||||||
must raise NotImplemented for missing package search method
|
must raise NotImplemented for missing package search method
|
||||||
"""
|
"""
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
remote.package_search("package", pacman=pacman, search_by=None)
|
remote.package_search("package", pacman=pacman)
|
||||||
|
@ -282,10 +282,3 @@ def test_packages_with_provides(pacman: Pacman) -> None:
|
|||||||
"""
|
"""
|
||||||
assert "sh" in pacman.packages()
|
assert "sh" in pacman.packages()
|
||||||
assert "mysql" in pacman.packages() # mariadb
|
assert "mysql" in pacman.packages() # mariadb
|
||||||
|
|
||||||
|
|
||||||
def test_package_provided_by(pacman: Pacman) -> None:
|
|
||||||
"""
|
|
||||||
must search through the provides lists
|
|
||||||
"""
|
|
||||||
assert list(pacman.provided_by("sh"))
|
|
||||||
|
@ -102,15 +102,6 @@ def test_check_loaded_architecture(configuration: Configuration) -> None:
|
|||||||
configuration.check_loaded()
|
configuration.check_loaded()
|
||||||
|
|
||||||
|
|
||||||
def test_copy_from(configuration: Configuration) -> None:
|
|
||||||
"""
|
|
||||||
must copy values from another instance
|
|
||||||
"""
|
|
||||||
instance = Configuration()
|
|
||||||
instance.copy_from(configuration)
|
|
||||||
assert instance.dump() == configuration.dump()
|
|
||||||
|
|
||||||
|
|
||||||
def test_dump(configuration: Configuration) -> None:
|
def test_dump(configuration: Configuration) -> None:
|
||||||
"""
|
"""
|
||||||
dump must not be empty
|
dump must not be empty
|
||||||
|
@ -62,8 +62,8 @@ def test_validate_is_ip_address(validator: Validator, mocker: MockerFixture) ->
|
|||||||
validator._validate_is_ip_address([], "field", "localhost")
|
validator._validate_is_ip_address([], "field", "localhost")
|
||||||
|
|
||||||
validator._validate_is_ip_address([], "field", "127.0.0.1")
|
validator._validate_is_ip_address([], "field", "127.0.0.1")
|
||||||
validator._validate_is_ip_address([], "field", "::") # nosec
|
validator._validate_is_ip_address([], "field", "::")
|
||||||
validator._validate_is_ip_address([], "field", "0.0.0.0") # nosec
|
validator._validate_is_ip_address([], "field", "0.0.0.0")
|
||||||
|
|
||||||
validator._validate_is_ip_address([], "field", "random string")
|
validator._validate_is_ip_address([], "field", "random string")
|
||||||
|
|
||||||
|
@ -195,32 +195,6 @@ def test_tree_levels_sorted() -> None:
|
|||||||
assert third == [leaf2.package, leaf4.package]
|
assert third == [leaf2.package, leaf4.package]
|
||||||
|
|
||||||
|
|
||||||
def test_tree_levels_provides() -> None:
|
|
||||||
"""
|
|
||||||
must build tree according to provides list
|
|
||||||
"""
|
|
||||||
leaf1 = Leaf(
|
|
||||||
Package(
|
|
||||||
base="package1",
|
|
||||||
version="1.0.0",
|
|
||||||
remote=RemoteSource(source=PackageSource.AUR),
|
|
||||||
packages={"package1": PackageDescription(depends=["package3"])},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
leaf2 = Leaf(
|
|
||||||
Package(
|
|
||||||
base="package2",
|
|
||||||
version="1.0.0",
|
|
||||||
remote=RemoteSource(source=PackageSource.AUR),
|
|
||||||
packages={"package2": PackageDescription(provides=["package3"])},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
first, second = Tree([leaf1, leaf2]).levels()
|
|
||||||
assert first == [leaf2.package]
|
|
||||||
assert second == [leaf1.package]
|
|
||||||
|
|
||||||
|
|
||||||
def test_tree_partitions() -> None:
|
def test_tree_partitions() -> None:
|
||||||
"""
|
"""
|
||||||
must divide tree into partitions
|
must divide tree into partitions
|
||||||
|
@ -150,13 +150,6 @@ def test_check_output_empty_line(mocker: MockerFixture) -> None:
|
|||||||
logger_mock.assert_has_calls([MockCall(""), MockCall("hello")])
|
logger_mock.assert_has_calls([MockCall(""), MockCall("hello")])
|
||||||
|
|
||||||
|
|
||||||
def test_check_output_encoding_error(resource_path_root: Path) -> None:
|
|
||||||
"""
|
|
||||||
must correctly process unicode encoding error in command output
|
|
||||||
"""
|
|
||||||
assert check_output("cat", str(resource_path_root / "models" / "package_pacman-static_pkgbuild"))
|
|
||||||
|
|
||||||
|
|
||||||
def test_check_user(repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
def test_check_user(repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must check user correctly
|
must check user correctly
|
||||||
|
@ -167,26 +167,15 @@ def test_from_aur(package_ahriman: Package, aur_package_ahriman: AURPackage, moc
|
|||||||
"""
|
"""
|
||||||
must construct package from aur
|
must construct package from aur
|
||||||
"""
|
"""
|
||||||
info_mock = mocker.patch("ahriman.core.alpm.remote.AUR.info", return_value=aur_package_ahriman)
|
mocker.patch("ahriman.core.alpm.remote.AUR.info", return_value=aur_package_ahriman)
|
||||||
|
|
||||||
package = Package.from_aur(package_ahriman.base, package_ahriman.packager)
|
package = Package.from_aur(package_ahriman.base, package_ahriman.packager)
|
||||||
info_mock.assert_called_once_with(package_ahriman.base, include_provides=False)
|
|
||||||
assert package_ahriman.base == package.base
|
assert package_ahriman.base == package.base
|
||||||
assert package_ahriman.version == package.version
|
assert package_ahriman.version == package.version
|
||||||
assert package_ahriman.packages.keys() == package.packages.keys()
|
assert package_ahriman.packages.keys() == package.packages.keys()
|
||||||
assert package_ahriman.packager == package.packager
|
assert package_ahriman.packager == package.packager
|
||||||
|
|
||||||
|
|
||||||
def test_from_aur_include_provides(package_ahriman: Package, aur_package_ahriman: AURPackage,
|
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must construct package from aur by using provides list
|
|
||||||
"""
|
|
||||||
info_mock = mocker.patch("ahriman.core.alpm.remote.AUR.info", return_value=aur_package_ahriman)
|
|
||||||
Package.from_aur(package_ahriman.base, package_ahriman.packager, include_provides=True)
|
|
||||||
info_mock.assert_called_once_with(package_ahriman.base, include_provides=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_from_build(package_ahriman: Package, mocker: MockerFixture, resource_path_root: Path) -> None:
|
def test_from_build(package_ahriman: Package, mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||||
"""
|
"""
|
||||||
must construct package from PKGBUILD
|
must construct package from PKGBUILD
|
||||||
@ -280,25 +269,14 @@ def test_from_json_view_3(package_tpacpi_bat_git: Package) -> None:
|
|||||||
assert Package.from_json(package_tpacpi_bat_git.view()) == package_tpacpi_bat_git
|
assert Package.from_json(package_tpacpi_bat_git.view()) == package_tpacpi_bat_git
|
||||||
|
|
||||||
|
|
||||||
def test_from_official_include_provides(package_ahriman: Package, aur_package_ahriman: AURPackage, pacman: Pacman,
|
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must construct package from official repository
|
|
||||||
"""
|
|
||||||
info_mock = mocker.patch("ahriman.core.alpm.remote.Official.info", return_value=aur_package_ahriman)
|
|
||||||
Package.from_official(package_ahriman.base, pacman, package_ahriman.packager, include_provides=True)
|
|
||||||
info_mock.assert_called_once_with(package_ahriman.base, pacman=pacman, include_provides=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage, pacman: Pacman,
|
def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage, pacman: Pacman,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must construct package from official repository
|
must construct package from official repository
|
||||||
"""
|
"""
|
||||||
info_mock = mocker.patch("ahriman.core.alpm.remote.Official.info", return_value=aur_package_ahriman)
|
mocker.patch("ahriman.core.alpm.remote.Official.info", return_value=aur_package_ahriman)
|
||||||
|
|
||||||
package = Package.from_official(package_ahriman.base, pacman, package_ahriman.packager)
|
package = Package.from_official(package_ahriman.base, pacman, package_ahriman.packager)
|
||||||
info_mock.assert_called_once_with(package_ahriman.base, pacman=pacman, include_provides=False)
|
|
||||||
assert package_ahriman.base == package.base
|
assert package_ahriman.base == package.base
|
||||||
assert package_ahriman.version == package.version
|
assert package_ahriman.version == package.version
|
||||||
assert package_ahriman.packages.keys() == package.packages.keys()
|
assert package_ahriman.packages.keys() == package.packages.keys()
|
||||||
|
@ -4,8 +4,6 @@ from aiohttp.test_utils import TestClient
|
|||||||
from aiohttp.web import Response
|
from aiohttp.web import Response
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
import ahriman.web.middlewares.metrics_handler as metrics_handler
|
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.v1.status.metrics import MetricsView
|
from ahriman.web.views.v1.status.metrics import MetricsView
|
||||||
|
|
||||||
@ -34,17 +32,4 @@ async def test_get(client: TestClient, mocker: MockerFixture) -> None:
|
|||||||
|
|
||||||
response = await client.get("/api/v1/metrics")
|
response = await client.get("/api/v1/metrics")
|
||||||
assert response.ok
|
assert response.ok
|
||||||
# there is no response validation here, because it is free text, so we check call instead
|
|
||||||
metrics_mock.assert_called_once_with(pytest.helpers.anyvar(int))
|
metrics_mock.assert_called_once_with(pytest.helpers.anyvar(int))
|
||||||
|
|
||||||
|
|
||||||
async def test_get_not_found(client: TestClient, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must return 404 error if no module found
|
|
||||||
"""
|
|
||||||
mocker.patch.object(metrics_handler, "aiohttp_openmetrics", None)
|
|
||||||
response_schema = pytest.helpers.schema_response(MetricsView.get, code=404)
|
|
||||||
|
|
||||||
response = await client.get("/api/v1/metrics")
|
|
||||||
assert response.status == 404
|
|
||||||
assert not response_schema.validate(await response.json())
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[settings]
|
[settings]
|
||||||
include = .
|
include = .
|
||||||
logging = logging.ini
|
logging = logging.ini
|
||||||
|
database = ../../../ahriman-test.db
|
||||||
|
|
||||||
[alpm]
|
[alpm]
|
||||||
database = /var/lib/pacman
|
database = /var/lib/pacman
|
||||||
@ -30,6 +31,7 @@ triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distr
|
|||||||
|
|
||||||
[repository]
|
[repository]
|
||||||
name = aur
|
name = aur
|
||||||
|
root = ../../../
|
||||||
|
|
||||||
[sign]
|
[sign]
|
||||||
target =
|
target =
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2021-2025 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/>.
|
|
||||||
#
|
|
119
tox.ini
Normal file
119
tox.ini
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
[tox]
|
||||||
|
envlist = check, tests
|
||||||
|
isolated_build = true
|
||||||
|
labels =
|
||||||
|
release = version, docs, publish
|
||||||
|
dependencies = -e .[journald,pacman,reports,s3,shell,stats,unixsocket,validator,web,web_api-docs,web_auth,web_oauth2,web_metrics]
|
||||||
|
project_name = ahriman
|
||||||
|
|
||||||
|
[mypy]
|
||||||
|
flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
||||||
|
asyncio_default_fixture_loop_scope = function
|
||||||
|
asyncio_mode = auto
|
||||||
|
spec_test_format = {result} {docstring_summary}
|
||||||
|
|
||||||
|
[testenv:archive]
|
||||||
|
description = Create source files tarball
|
||||||
|
deps =
|
||||||
|
build
|
||||||
|
commands =
|
||||||
|
python -m build --sdist
|
||||||
|
|
||||||
|
[testenv:check]
|
||||||
|
description = Run common checks like linter, mypy, etc
|
||||||
|
dependency_groups =
|
||||||
|
check
|
||||||
|
deps =
|
||||||
|
{[tox]dependencies}
|
||||||
|
pip_pre = true
|
||||||
|
setenv =
|
||||||
|
CFLAGS="-Wno-unterminated-string-initialization"
|
||||||
|
MYPYPATH=src
|
||||||
|
commands =
|
||||||
|
autopep8 --exit-code --max-line-length 120 -aa -i -j 0 -r "src/{[tox]project_name}" "tests/{[tox]project_name}"
|
||||||
|
pylint --rcfile=.pylintrc "src/{[tox]project_name}"
|
||||||
|
bandit -c .bandit.yml -r "src/{[tox]project_name}"
|
||||||
|
bandit -c .bandit-test.yml -r "tests/{[tox]project_name}"
|
||||||
|
mypy {[mypy]flags} -p "{[tox]project_name}" --install-types --non-interactive
|
||||||
|
|
||||||
|
[testenv:docs]
|
||||||
|
description = Generate source files for documentation
|
||||||
|
allowlist_externals =
|
||||||
|
bash
|
||||||
|
find
|
||||||
|
mv
|
||||||
|
changedir = src
|
||||||
|
dependency_groups =
|
||||||
|
docs
|
||||||
|
depends =
|
||||||
|
version
|
||||||
|
deps =
|
||||||
|
{[tox]dependencies}
|
||||||
|
uv
|
||||||
|
pip_pre = true
|
||||||
|
setenv =
|
||||||
|
SPHINX_APIDOC_OPTIONS=members,no-undoc-members,show-inheritance
|
||||||
|
commands =
|
||||||
|
bash -c 'shtab --shell bash --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/bash-completion/completions/_ahriman'
|
||||||
|
bash -c 'shtab --shell zsh --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/zsh/site-functions/_ahriman'
|
||||||
|
argparse-manpage --module ahriman.application.ahriman --function _parser --author "ahriman team" --project-name ahriman --author-email "" --url https://github.com/arcan1s/ahriman --output ../package/share/man/man1/ahriman.1
|
||||||
|
pydeps ahriman --no-output --show-dot --dot-output architecture.dot --no-config --cluster
|
||||||
|
mv architecture.dot ../docs/_static/architecture.dot
|
||||||
|
# remove autogenerated modules rst files
|
||||||
|
find ../docs -type f -name "{[tox]project_name}*.rst" -delete
|
||||||
|
sphinx-apidoc -o ../docs .
|
||||||
|
# compile list of dependencies for rtd.io
|
||||||
|
uv pip compile --group ../pyproject.toml:docs --extra s3 --extra validator --extra web --output-file ../docs/requirements.txt --quiet ../pyproject.toml
|
||||||
|
|
||||||
|
[testenv:html]
|
||||||
|
description = Generate html documentation
|
||||||
|
dependency_groups =
|
||||||
|
docs
|
||||||
|
deps =
|
||||||
|
{[tox]dependencies}
|
||||||
|
pip_pre = true
|
||||||
|
recreate = true
|
||||||
|
commands =
|
||||||
|
sphinx-build -b html -a -j auto -W docs {envtmpdir}{/}html
|
||||||
|
|
||||||
|
[testenv:publish]
|
||||||
|
description = Create and publish release to GitHub
|
||||||
|
allowlist_externals =
|
||||||
|
git
|
||||||
|
depends =
|
||||||
|
docs
|
||||||
|
passenv =
|
||||||
|
SSH_AUTH_SOCK
|
||||||
|
commands =
|
||||||
|
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/_static/architecture.dot package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
|
||||||
|
git commit -m "Release {posargs}"
|
||||||
|
git tag "{posargs}"
|
||||||
|
git push
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
[testenv:tests]
|
||||||
|
description = Run tests
|
||||||
|
dependency_groups =
|
||||||
|
tests
|
||||||
|
deps =
|
||||||
|
{[tox]dependencies}
|
||||||
|
pip_pre = true
|
||||||
|
setenv =
|
||||||
|
CFLAGS="-Wno-unterminated-string-initialization"
|
||||||
|
commands =
|
||||||
|
pytest {posargs}
|
||||||
|
|
||||||
|
[testenv:version]
|
||||||
|
description = Bump package version
|
||||||
|
allowlist_externals =
|
||||||
|
sed
|
||||||
|
deps =
|
||||||
|
packaging
|
||||||
|
commands =
|
||||||
|
# check if version is set and validate it
|
||||||
|
{envpython} -c 'from packaging.version import Version; Version("{posargs}")'
|
||||||
|
sed -i 's/^__version__ = .*/__version__ = "{posargs}"/' src/ahriman/__init__.py
|
||||||
|
sed -i "s/pkgver=.*/pkgver={posargs}/" package/archlinux/PKGBUILD
|
310
tox.toml
310
tox.toml
@ -1,310 +0,0 @@
|
|||||||
env_list = [
|
|
||||||
"check",
|
|
||||||
"tests",
|
|
||||||
]
|
|
||||||
isolated_build = true
|
|
||||||
labels.release = [
|
|
||||||
"version",
|
|
||||||
"docs",
|
|
||||||
"publish",
|
|
||||||
]
|
|
||||||
|
|
||||||
[flags]
|
|
||||||
autopep8 = [
|
|
||||||
"--max-line-length", "120",
|
|
||||||
"-aa",
|
|
||||||
]
|
|
||||||
bandit = [
|
|
||||||
"--configfile", ".bandit.yml",
|
|
||||||
]
|
|
||||||
manpage = [
|
|
||||||
"--author", "{[project]name} team",
|
|
||||||
"--author-email", "",
|
|
||||||
"--description", "ArcH linux ReposItory MANager",
|
|
||||||
"--manual-title", "ArcH linux ReposItory MANager",
|
|
||||||
"--project-name", "{[project]name}",
|
|
||||||
"--version", "{env:VERSION}",
|
|
||||||
"--url", "https://github.com/arcan1s/ahriman",
|
|
||||||
]
|
|
||||||
mypy = [
|
|
||||||
"--implicit-reexport",
|
|
||||||
"--strict",
|
|
||||||
"--allow-untyped-decorators",
|
|
||||||
"--allow-subclassing-any",
|
|
||||||
]
|
|
||||||
pydeps = [
|
|
||||||
"--no-config",
|
|
||||||
"--cluster",
|
|
||||||
]
|
|
||||||
pylint = [
|
|
||||||
"--rcfile", ".pylint.toml",
|
|
||||||
]
|
|
||||||
shtab = [
|
|
||||||
"--prefix", "{[project]name}",
|
|
||||||
"--prog", "{[project]name}",
|
|
||||||
"ahriman.application.ahriman._parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project]
|
|
||||||
extras = [
|
|
||||||
"journald",
|
|
||||||
"pacman",
|
|
||||||
"reports",
|
|
||||||
"s3",
|
|
||||||
"shell",
|
|
||||||
"stats",
|
|
||||||
"unixsocket",
|
|
||||||
"validator",
|
|
||||||
"web",
|
|
||||||
"web-auth",
|
|
||||||
"web-docs",
|
|
||||||
"web-oauth2",
|
|
||||||
"web-metrics",
|
|
||||||
]
|
|
||||||
name = "ahriman"
|
|
||||||
|
|
||||||
[env.archive]
|
|
||||||
description = "Create source files tarball"
|
|
||||||
deps = [
|
|
||||||
"build",
|
|
||||||
]
|
|
||||||
commands = [
|
|
||||||
[
|
|
||||||
"{envpython}",
|
|
||||||
"-m", "build",
|
|
||||||
"--sdist",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
[env.check]
|
|
||||||
description = "Run common checks like linter, mypy, etc"
|
|
||||||
dependency_groups = [
|
|
||||||
"check",
|
|
||||||
]
|
|
||||||
extras = [
|
|
||||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
|
||||||
]
|
|
||||||
pip_pre = true
|
|
||||||
set_env.CFLAGS = "-Wno-unterminated-string-initialization"
|
|
||||||
set_env.MYPYPATH = "src"
|
|
||||||
commands = [
|
|
||||||
[
|
|
||||||
"autopep8",
|
|
||||||
{ replace = "ref", of = ["flags", "autopep8"], extend = true },
|
|
||||||
"--exit-code",
|
|
||||||
"--in-place",
|
|
||||||
"--jobs", "0",
|
|
||||||
"--recursive",
|
|
||||||
"src/{[project]name}",
|
|
||||||
"tests/{[project]name}",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"pylint",
|
|
||||||
{ replace = "ref", of = ["flags", "pylint"], extend = true },
|
|
||||||
"src/{[project]name}",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"bandit",
|
|
||||||
{ replace = "ref", of = ["flags", "bandit"], extend = true },
|
|
||||||
"--recursive",
|
|
||||||
"src/{[project]name}",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"bandit",
|
|
||||||
{ replace = "ref", of = ["flags", "bandit"], extend = true },
|
|
||||||
"--skip", "B101,B105,B106",
|
|
||||||
"--recursive",
|
|
||||||
"src/{[project]name}",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"mypy",
|
|
||||||
{ replace = "ref", of = ["flags", "mypy"], extend = true },
|
|
||||||
"--install-types",
|
|
||||||
"--non-interactive",
|
|
||||||
"--package", "{[project]name}",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
[env.docs]
|
|
||||||
description = "Generate source files for documentation"
|
|
||||||
dependency_groups = [
|
|
||||||
"docs",
|
|
||||||
]
|
|
||||||
depends = [
|
|
||||||
"version",
|
|
||||||
]
|
|
||||||
deps = [
|
|
||||||
"uv",
|
|
||||||
]
|
|
||||||
dynamic_version = "{[project]name}.__version__"
|
|
||||||
extras = [
|
|
||||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
|
||||||
]
|
|
||||||
# TODO: steamline shlex usage after https://github.com/iterative/shtab/pull/192 merge
|
|
||||||
handle_redirect = true
|
|
||||||
pip_pre = true
|
|
||||||
set_env.PYTHONPATH = "src"
|
|
||||||
set_env.SPHINX_APIDOC_OPTIONS = "members,no-undoc-members,show-inheritance"
|
|
||||||
commands = [
|
|
||||||
[
|
|
||||||
"shtab",
|
|
||||||
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
|
||||||
"--shell",
|
|
||||||
"bash",
|
|
||||||
">",
|
|
||||||
"package/share/bash-completion/completions/_ahriman",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"shtab",
|
|
||||||
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
|
||||||
"--shell",
|
|
||||||
"zsh",
|
|
||||||
">",
|
|
||||||
"package/share/zsh/site-functions/_ahriman",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"argparse-manpage",
|
|
||||||
{ replace = "ref", of = ["flags", "manpage"], extend = true },
|
|
||||||
"--module", "ahriman.application.ahriman",
|
|
||||||
"--function", "_parser",
|
|
||||||
"--output", "package/share/man/man1/ahriman.1",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"pydeps",
|
|
||||||
{ replace = "ref", of = ["flags", "pydeps"], extend = true },
|
|
||||||
"--dot-output", "{tox_root}/docs/_static/architecture.dot",
|
|
||||||
"--no-output",
|
|
||||||
"--show-dot",
|
|
||||||
"src/ahriman",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"sphinx-apidoc",
|
|
||||||
"--force",
|
|
||||||
"--no-toc",
|
|
||||||
"--output-dir", "docs",
|
|
||||||
"src",
|
|
||||||
],
|
|
||||||
# compile list of dependencies for rtd.io
|
|
||||||
[
|
|
||||||
"uv",
|
|
||||||
"pip",
|
|
||||||
"compile",
|
|
||||||
"--group", "pyproject.toml:docs",
|
|
||||||
"--extra", "s3",
|
|
||||||
"--extra", "validator",
|
|
||||||
"--extra", "web",
|
|
||||||
"--output-file", "docs/requirements.txt",
|
|
||||||
"--quiet",
|
|
||||||
"pyproject.toml",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
[env.html]
|
|
||||||
description = "Generate html documentation"
|
|
||||||
dependency_groups = [
|
|
||||||
"docs",
|
|
||||||
]
|
|
||||||
extras = [
|
|
||||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
|
||||||
]
|
|
||||||
pip_pre = true
|
|
||||||
recreate = true
|
|
||||||
commands = [
|
|
||||||
[
|
|
||||||
"sphinx-build",
|
|
||||||
"--builder", "html",
|
|
||||||
"--fail-on-warning",
|
|
||||||
"--jobs", "auto",
|
|
||||||
"--write-all",
|
|
||||||
"docs",
|
|
||||||
"{envtmpdir}/html",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
[env.publish]
|
|
||||||
description = "Create and publish release to GitHub"
|
|
||||||
allowlist_externals = [
|
|
||||||
"git",
|
|
||||||
]
|
|
||||||
depends = [
|
|
||||||
"docs",
|
|
||||||
]
|
|
||||||
pass_env = [
|
|
||||||
"SSH_AUTH_SOCK",
|
|
||||||
]
|
|
||||||
commands = [
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
"add",
|
|
||||||
"package/archlinux/PKGBUILD",
|
|
||||||
"src/ahriman/__init__.py",
|
|
||||||
"docs/_static/architecture.dot",
|
|
||||||
"package/share/man/man1/ahriman.1",
|
|
||||||
"package/share/bash-completion/completions/_ahriman",
|
|
||||||
"package/share/zsh/site-functions/_ahriman",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
"commit",
|
|
||||||
"--message", "Release {posargs}",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
"tag",
|
|
||||||
"{posargs}",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
"push",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
"push",
|
|
||||||
"--tags",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
[env.tests]
|
|
||||||
description = "Run tests"
|
|
||||||
dependency_groups = [
|
|
||||||
"tests",
|
|
||||||
]
|
|
||||||
extras = [
|
|
||||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
|
||||||
]
|
|
||||||
pip_pre = true
|
|
||||||
set_env.CFLAGS = "-Wno-unterminated-string-initialization"
|
|
||||||
commands = [
|
|
||||||
[
|
|
||||||
"pytest",
|
|
||||||
{ replace = "posargs", extend = true },
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
[env.version]
|
|
||||||
description = "Bump package version"
|
|
||||||
allowlist_externals = [
|
|
||||||
"sed",
|
|
||||||
]
|
|
||||||
deps = [
|
|
||||||
"packaging",
|
|
||||||
]
|
|
||||||
commands = [
|
|
||||||
# check if version is set and validate it
|
|
||||||
[
|
|
||||||
"{envpython}",
|
|
||||||
"-c", "from packaging.version import Version; Version('{posargs}')",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"sed",
|
|
||||||
"--in-place",
|
|
||||||
"s/^__version__ = .*/__version__ = \"{posargs}\"/",
|
|
||||||
"src/ahriman/__init__.py",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"sed",
|
|
||||||
"--in-place",
|
|
||||||
"s/pkgver=.*/pkgver={posargs}/",
|
|
||||||
"package/archlinux/PKGBUILD",
|
|
||||||
],
|
|
||||||
]
|
|
128
toxfile.py
128
toxfile.py
@ -1,128 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2021-2025 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 importlib
|
|
||||||
import shlex
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from tox.config.sets import EnvConfigSet
|
|
||||||
from tox.config.types import Command
|
|
||||||
from tox.plugin import impl
|
|
||||||
from tox.session.state import State
|
|
||||||
from tox.tox_env.api import ToxEnv
|
|
||||||
|
|
||||||
|
|
||||||
def _extract_version(env_conf: EnvConfigSet, python_path: str | None = None) -> dict[str, str]:
|
|
||||||
"""
|
|
||||||
extract version dynamically and set VERSION environment variable
|
|
||||||
|
|
||||||
Args:
|
|
||||||
env_conf(EnvConfigSet): the core configuration object
|
|
||||||
python_path(str | None): python path variable if available
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict[str, str]: environment variables which must be inserted
|
|
||||||
"""
|
|
||||||
import_path = env_conf["dynamic_version"]
|
|
||||||
if not import_path:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
if python_path is not None:
|
|
||||||
sys.path.append(python_path)
|
|
||||||
|
|
||||||
module_name, variable_name = import_path.rsplit(".", maxsplit=1)
|
|
||||||
module = importlib.import_module(module_name)
|
|
||||||
version = getattr(module, variable_name)
|
|
||||||
|
|
||||||
# reset import paths
|
|
||||||
sys.path.pop()
|
|
||||||
|
|
||||||
return {"VERSION": version}
|
|
||||||
|
|
||||||
|
|
||||||
def _wrap_commands(env_conf: EnvConfigSet, shell: str = "bash") -> None:
|
|
||||||
"""
|
|
||||||
wrap commands into shell if there is redirect
|
|
||||||
|
|
||||||
Args:
|
|
||||||
env_conf(EnvConfigSet): the core configuration object
|
|
||||||
shell(str, optional): shell command to use (Default value = "bash")
|
|
||||||
"""
|
|
||||||
if not env_conf["handle_redirect"]:
|
|
||||||
return
|
|
||||||
|
|
||||||
# append shell just in case
|
|
||||||
env_conf["allowlist_externals"].append(shell)
|
|
||||||
|
|
||||||
for command in env_conf["commands"]:
|
|
||||||
if len(command.args) < 3: # command itself, redirect and output
|
|
||||||
continue
|
|
||||||
|
|
||||||
redirect, output = command.args[-2:]
|
|
||||||
if redirect not in (">", "2>", "&>"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
command.args = [
|
|
||||||
shell,
|
|
||||||
"-c",
|
|
||||||
f"{Command(command.args[:-2]).shell} {redirect} {shlex.quote(output)}",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@impl
|
|
||||||
def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
|
|
||||||
"""
|
|
||||||
add a command line argument. This is the first hook to be called,
|
|
||||||
right after the logging setup and config source discovery.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
env_conf(EnvConfigSet): the core configuration object
|
|
||||||
state(State): the global tox state object
|
|
||||||
"""
|
|
||||||
del state
|
|
||||||
|
|
||||||
env_conf.add_config(
|
|
||||||
keys=["dynamic_version"],
|
|
||||||
of_type=str,
|
|
||||||
default="",
|
|
||||||
desc="import path for the version variable",
|
|
||||||
)
|
|
||||||
env_conf.add_config(
|
|
||||||
keys=["handle_redirect"],
|
|
||||||
of_type=bool,
|
|
||||||
default=False,
|
|
||||||
desc="wrap commands to handle redirects if any",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@impl
|
|
||||||
def tox_before_run_commands(tox_env: ToxEnv) -> None:
|
|
||||||
"""
|
|
||||||
called before the commands set is executed
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tox_env(ToxEnv): the tox environment being executed
|
|
||||||
"""
|
|
||||||
env_conf = tox_env.conf
|
|
||||||
set_env = env_conf["set_env"]
|
|
||||||
|
|
||||||
python_path = set_env.load("PYTHONPATH") if "PYTHONPATH" in set_env else None
|
|
||||||
set_env.update(_extract_version(env_conf, python_path))
|
|
||||||
|
|
||||||
_wrap_commands(env_conf)
|
|
Reference in New Issue
Block a user