mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 06:41:43 +00:00
Compare commits
2 Commits
e119f092b4
...
7769a4a6e0
Author | SHA1 | Date | |
---|---|---|---|
7769a4a6e0 | |||
066d1b1dde |
@ -1,6 +0,0 @@
|
|||||||
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@v2
|
- uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to docker hub
|
- name: Login to docker hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
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@v2
|
uses: docker/login-action@v3
|
||||||
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@v3
|
uses: docker/metadata-action@v5
|
||||||
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@v4
|
uses: docker/build-push-action@v6
|
||||||
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,8 +37,6 @@ 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@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Extract version
|
- name: Extract version
|
||||||
id: version
|
id: version
|
||||||
@ -27,8 +27,7 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
filter: 'Release \d+\.\d+\.\d+'
|
filter: 'Release \d+\.\d+\.\d+'
|
||||||
|
|
||||||
- name: Install dependencies
|
- uses: ConorMacBride/install-package@v1.1.0
|
||||||
uses: ConorMacBride/install-package@v1.1.0
|
|
||||||
with:
|
with:
|
||||||
apt: tox
|
apt: tox
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ jobs:
|
|||||||
VERSION: ${{ steps.version.outputs.VERSION }}
|
VERSION: ${{ steps.version.outputs.VERSION }}
|
||||||
|
|
||||||
- name: Publish release
|
- name: Publish release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
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@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- 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@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- 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
10
.github/workflows/tests.sh
vendored
@ -1,10 +0,0 @@
|
|||||||
#!/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,7 +26,16 @@ jobs:
|
|||||||
- ${{ github.workspace }}:/build
|
- ${{ github.workspace }}:/build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- run: pacman --noconfirm -Syu base-devel git python-tox
|
||||||
|
|
||||||
- name: Run check and tests in arch linux container
|
- run: git config --global --add safe.directory *
|
||||||
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)" ]
|
||||||
|
2621
docs/_static/architecture.dot
vendored
2621
docs/_static/architecture.dot
vendored
File diff suppressed because it is too large
Load Diff
@ -413,10 +413,11 @@ 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), ``aiohttp_cors`` (CORS support, required by documentation).
|
* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation, optional), ``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.2
|
pkgver=2.18.3
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="ArcH linux ReposItory MANager"
|
pkgdesc="ArcH linux ReposItory MANager"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
@ -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.2"
|
__version__ = "2.18.3"
|
||||||
|
@ -210,6 +210,17 @@ 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
|
||||||
@ -220,6 +231,7 @@ 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
|
||||||
|
@ -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"), name="_static",
|
application.router.add_static("/static", configuration.getpath("web", "static_path"),
|
||||||
follow_symlinks=True)
|
name="_static", 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))
|
||||||
|
@ -144,6 +144,7 @@ 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")
|
||||||
|
|
||||||
@ -159,6 +160,7 @@ 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"})
|
||||||
@ -175,6 +177,7 @@ 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())
|
||||||
@ -191,6 +194,7 @@ 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")
|
||||||
@ -207,6 +211,7 @@ 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())
|
||||||
|
|
||||||
@ -221,6 +226,7 @@ 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")
|
||||||
|
|
||||||
@ -236,6 +242,7 @@ 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")
|
||||||
|
|
||||||
@ -251,6 +258,7 @@ 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,6 +6,7 @@ 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
|
||||||
@ -30,11 +31,12 @@ 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,
|
||||||
package_ahriman: Package, mocker: MockerFixture) -> None:
|
database: SQLite, 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")
|
||||||
@ -51,12 +53,13 @@ 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,
|
||||||
package_ahriman: Package, mocker: MockerFixture) -> None:
|
database: SQLite, 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")
|
||||||
@ -69,12 +72,14 @@ 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,
|
||||||
mocker: MockerFixture) -> None:
|
database: SQLite, 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,6 +9,7 @@ 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
|
||||||
@ -44,11 +45,12 @@ 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,
|
||||||
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
database: SQLite, 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")
|
||||||
@ -88,12 +90,13 @@ 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,
|
||||||
mocker: MockerFixture) -> None:
|
database: SQLite, 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,7 +51,8 @@ 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, mocker: MockerFixture) -> None:
|
def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, database: SQLite,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must process users with empty password salt
|
must process users with empty password salt
|
||||||
"""
|
"""
|
||||||
@ -59,6 +60,7 @@ 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,6 +1575,7 @@ 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,19 +249,25 @@ def auth(configuration: Configuration) -> Auth:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def configuration(repository_id: RepositoryId, resource_path_root: Path) -> Configuration:
|
def configuration(repository_id: RepositoryId, tmp_path: Path, 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
|
||||||
@ -275,9 +281,7 @@ def database(configuration: Configuration) -> SQLite:
|
|||||||
Returns:
|
Returns:
|
||||||
SQLite: database test instance
|
SQLite: database test instance
|
||||||
"""
|
"""
|
||||||
database = SQLite.load(configuration)
|
return SQLite.load(configuration)
|
||||||
yield database
|
|
||||||
database.path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -102,6 +102,15 @@ 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", "::")
|
validator._validate_is_ip_address([], "field", "::") # nosec
|
||||||
validator._validate_is_ip_address([], "field", "0.0.0.0")
|
validator._validate_is_ip_address([], "field", "0.0.0.0") # nosec
|
||||||
|
|
||||||
validator._validate_is_ip_address([], "field", "random string")
|
validator._validate_is_ip_address([], "field", "random string")
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
[settings]
|
[settings]
|
||||||
include = .
|
include = .
|
||||||
logging = logging.ini
|
logging = logging.ini
|
||||||
database = ../../../ahriman-test.db
|
|
||||||
|
|
||||||
[alpm]
|
[alpm]
|
||||||
database = /var/lib/pacman
|
database = /var/lib/pacman
|
||||||
@ -31,7 +30,6 @@ triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distr
|
|||||||
|
|
||||||
[repository]
|
[repository]
|
||||||
name = aur
|
name = aur
|
||||||
root = ../../../
|
|
||||||
|
|
||||||
[sign]
|
[sign]
|
||||||
target =
|
target =
|
||||||
|
40
tox.ini
40
tox.ini
@ -6,8 +6,14 @@ labels =
|
|||||||
dependencies = -e .[journald,pacman,reports,s3,shell,stats,unixsocket,validator,web,web_api-docs,web_auth,web_oauth2,web_metrics]
|
dependencies = -e .[journald,pacman,reports,s3,shell,stats,unixsocket,validator,web,web_api-docs,web_auth,web_oauth2,web_metrics]
|
||||||
project_name = ahriman
|
project_name = ahriman
|
||||||
|
|
||||||
[mypy]
|
[flags]
|
||||||
flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any
|
autopep8 = --max-line-length 120 -aa --in-place
|
||||||
|
bandit = --configfile .bandit.yml
|
||||||
|
manpage = --author "ahriman team" --author-email "" --description "ArcH linux ReposItory MANager" --manual-title "ArcH linux ReposItory MANager" --project-name ahriman --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 ahriman --prog ahriman ahriman.application.ahriman._parser
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
||||||
@ -33,19 +39,17 @@ setenv =
|
|||||||
CFLAGS="-Wno-unterminated-string-initialization"
|
CFLAGS="-Wno-unterminated-string-initialization"
|
||||||
MYPYPATH=src
|
MYPYPATH=src
|
||||||
commands =
|
commands =
|
||||||
autopep8 --exit-code --max-line-length 120 -aa -i -j 0 -r "src/{[tox]project_name}" "tests/{[tox]project_name}"
|
autopep8 {[flags]autopep8} --exit-code --jobs 0 --recursive "src/{[tox]project_name}" "tests/{[tox]project_name}"
|
||||||
pylint --rcfile=.pylint.toml "src/{[tox]project_name}"
|
pylint {[flags]pylint} "src/{[tox]project_name}"
|
||||||
bandit -c .bandit.yml -r "src/{[tox]project_name}"
|
bandit {[flags]bandit} --recursive "src/{[tox]project_name}"
|
||||||
bandit -c .bandit-test.yml -r "tests/{[tox]project_name}"
|
bandit {[flags]bandit} --skip B101,B105,B106 --recursive "tests/{[tox]project_name}"
|
||||||
mypy {[mypy]flags} -p "{[tox]project_name}" --install-types --non-interactive
|
mypy {[flags]mypy} --install-types --non-interactive --package "{[tox]project_name}"
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
description = Generate source files for documentation
|
description = Generate source files for documentation
|
||||||
allowlist_externals =
|
allowlist_externals =
|
||||||
bash
|
bash
|
||||||
find
|
find
|
||||||
mv
|
|
||||||
changedir = src
|
|
||||||
dependency_groups =
|
dependency_groups =
|
||||||
docs
|
docs
|
||||||
depends =
|
depends =
|
||||||
@ -55,18 +59,18 @@ deps =
|
|||||||
uv
|
uv
|
||||||
pip_pre = true
|
pip_pre = true
|
||||||
setenv =
|
setenv =
|
||||||
|
PYTHONPATH=src
|
||||||
SPHINX_APIDOC_OPTIONS=members,no-undoc-members,show-inheritance
|
SPHINX_APIDOC_OPTIONS=members,no-undoc-members,show-inheritance
|
||||||
commands =
|
commands =
|
||||||
bash -c 'shtab --shell bash --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/bash-completion/completions/_ahriman'
|
bash -c 'shtab {[flags]shtab} --shell bash > 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'
|
bash -c 'shtab {[flags]shtab} --shell zsh > 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
|
argparse-manpage {[flags]manpage} --module ahriman.application.ahriman --function _parser --output ../package/share/man/man1/ahriman.1
|
||||||
pydeps ahriman --no-output --show-dot --dot-output architecture.dot --no-config --cluster
|
pydeps {[flags]pydeps} --no-output --show-dot --dot-output {tox_root}{/}docs/_static/architecture.dot src/ahriman
|
||||||
mv architecture.dot ../docs/_static/architecture.dot
|
|
||||||
# remove autogenerated modules rst files
|
# remove autogenerated modules rst files
|
||||||
find ../docs -type f -name "{[tox]project_name}*.rst" -delete
|
find docs -type f -name "{[tox]project_name}*.rst" -delete
|
||||||
sphinx-apidoc -o ../docs .
|
sphinx-apidoc --output-dir docs src
|
||||||
# compile list of dependencies for rtd.io
|
# 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
|
uv pip compile --group pyproject.toml:docs --extra s3 --extra validator --extra web --output-file docs/requirements.txt --quiet pyproject.toml
|
||||||
|
|
||||||
[testenv:html]
|
[testenv:html]
|
||||||
description = Generate html documentation
|
description = Generate html documentation
|
||||||
@ -77,7 +81,7 @@ deps =
|
|||||||
pip_pre = true
|
pip_pre = true
|
||||||
recreate = true
|
recreate = true
|
||||||
commands =
|
commands =
|
||||||
sphinx-build -b html -a -j auto -W docs {envtmpdir}{/}html
|
sphinx-build --builder html --write-all --jobs auto --fail-on-warning docs {envtmpdir}{/}html
|
||||||
|
|
||||||
[testenv:publish]
|
[testenv:publish]
|
||||||
description = Create and publish release to GitHub
|
description = Create and publish release to GitHub
|
||||||
|
Reference in New Issue
Block a user