Compare commits

...

2 Commits

Author SHA1 Message Date
a1374dd477 review fixes 2024-11-01 16:02:11 +02:00
faef091959 add metapackage 2024-11-01 15:03:17 +02:00
17 changed files with 51 additions and 41 deletions

View File

@ -31,7 +31,7 @@ mv "dist/ahriman-$PKGVER.tar.gz" package/archlinux
chmod +777 package/archlinux # because fuck you that's why
cd package/archlinux
sudo -u nobody -- makepkg -cf --skipchecksums --noconfirm
sudo -u nobody -- makepkg --packagelist | grep "ahriman-$PKGVER" | pacman -U --noconfirm --nodeps -
sudo -u nobody -- makepkg --packagelist | grep "ahriman-core-$PKGVER" | pacman -U --noconfirm --nodeps -
if [[ -z $MINIMAL_INSTALL ]]; then
sudo -u nobody -- makepkg --packagelist | grep "ahriman-triggers-$PKGVER" | pacman -U --noconfirm --nodeps -
sudo -u nobody -- makepkg --packagelist | grep "ahriman-web-$PKGVER" | pacman -U --noconfirm --nodeps -

View File

@ -27,7 +27,7 @@ Let's imagine, that the new class implements ``help-web``, which prints server i
result = response.json()
print(result)
The main functionality of the class is already described, but command is still not available yet. To do so, it is required to set ``arguments`` property, which is list of functions, which takes argument parser object, creates new subcommand and returns the modified parser, e.g.:
The main functionality of the class is already described, but command is still not available yet. To do so, it is required to set ``arguments`` property, which is the list of the functions, each of them which takes argument parser object, creates new subcommand and returns the modified parser, e.g.:
.. code-block:: python

View File

@ -1,9 +1,9 @@
Writing own API endpoint
========================
The web service loads views dynamically, thus it is possible to add custom API endpoint or even web page. The views must be derived from ``ahriman.web.views.base.BaseView`` and implements HTTP methods. The API specification will be also loaded (if available), but optional. The implementation must be saved into the ``ahriman.web.views`` package
The web service loads views dynamically, thus it is possible to add custom API endpoint or even web page. The view must be derived from ``ahriman.web.views.base.BaseView`` and should implement desired HTTP methods. The API specification will be also loaded automatically if available, but optional. The implementation must be saved into the ``ahriman.web.views`` package
Let's consider example for API endpoint which always returns 204 with no response:
Let's consider example of API endpoint which always returns 204 with no response:
.. code-block:: python
@ -18,9 +18,9 @@ Let's consider example for API endpoint which always returns 204 with no respons
# check public methods of the BaseView class for all available controls
raise HTTPNoContent
The ``get()`` method can be decorated by ``aiohttp_apispec`` methods, but we will leave it for self-study. Consider checking examples of usages in the main package.
The ``get()`` method can be decorated by ``aiohttp_apispec`` methods, but we will leave it for a self-study, please, consider to check examples of usages in the main package.
In order to view to be set correctly to routes list, few more options are required to be set. First of all, it is required to specify ``ROUTES`` (list of strings), which contains list of all available routes, e.g.:
In order to view to be added to the route list correctly, few more properties are required to be set. First of all, it is required to specify ``ROUTES`` (list of strings), which contains list of all available routes, e.g.:
.. code-block:: python

View File

@ -440,7 +440,7 @@ Different APIs are separated into different packages:
* ``ahriman.web.views.api`` not a real API, but some views which provide OpenAPI support.
* ``ahriman.web.views.*.auditlog`` provides event log API.
* ``ahriman.web.views.*.distributed`` is an API for builders interaction for multi-node setup.
* ``ahriman.web.views.*.pacakges`` contains views which provide information about existing packages.
* ``ahriman.web.views.*.packages`` contains views which provide information about existing packages.
* ``ahriman.web.views.*.service`` provides views for application controls.
* ``ahriman.web.views.*.status`` package provides REST API for application reporting.
* ``ahriman.web.views.*.user`` package provides login and logout methods which can be called without authorization.

View File

@ -13,7 +13,7 @@ TL;DR
.. code-block:: shell
yay -S ahriman
yay -S ahriman-core
ahriman -a x86_64 -r aur service-setup --packager "ahriman bot <ahriman@example.com>"
systemctl enable --now ahriman@x86_64-aur.timer

View File

@ -9,7 +9,7 @@ How to setup web service
.. code-block:: shell
yay -S -ahriman-web
yay -S ahriman-web
#.
Configure service:

View File

@ -2,7 +2,7 @@ Initial setup
=============
#.
Install package as usual.
Install package(s) as usual. At least, ``ahriman-core`` package is required; other features can be installed separately. Alternatively, it is possible to install meta-package, which includes everything.
#.
Change settings if required, see :doc:`configuration reference <configuration>` for more details.
#.

View File

@ -1,7 +1,7 @@
# Maintainer: Evgeniy Alekseev
pkgbase='ahriman'
pkgname=('ahriman' 'ahriman-triggers' 'ahriman-web')
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
pkgver=2.15.2
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
@ -22,6 +22,12 @@ build() {
package_ahriman() {
pkgname='ahriman'
pkgdesc="ArcH linux ReposItory MANager (meta package)"
depends=("$pkgbase-core=$pkgver" "$pkgbase-triggers=$pkgver" "$pkgbase-web=$pkgver")
}
package_ahriman-core() {
pkgname='ahriman-core'
optdepends=('ahriman-triggers: additional extensions for the application'
'ahriman-web: web server'
'python-boto3: sync to s3'
@ -31,7 +37,7 @@ package_ahriman() {
'python-jinja: html report generation'
'python-systemd: journal support'
'rsync: sync by using rsync')
install="$pkgname.install"
install="$pkgbase.install"
backup=('etc/ahriman.ini'
'etc/ahriman.ini.d/logging.ini')
@ -51,7 +57,7 @@ package_ahriman() {
package_ahriman-triggers() {
pkgname='ahriman-triggers'
pkgdesc="ArcH linux ReposItory MANager, additional extensions"
depends=("$pkgbase=$pkgver")
depends=("$pkgbase-core=$pkgver")
backup=('etc/ahriman.ini.d/00-triggers.ini')
cd "$pkgbase-$pkgver"
@ -65,7 +71,7 @@ package_ahriman-triggers() {
package_ahriman-web() {
pkgname='ahriman-web'
pkgdesc="ArcH linux ReposItory MANager, web server"
depends=("$pkgbase=$pkgver" 'python-aiohttp-apispec>=3.0.0' 'python-aiohttp-cors' 'python-aiohttp-jinja2')
depends=("$pkgbase-core=$pkgver" 'python-aiohttp-apispec>=3.0.0' 'python-aiohttp-cors' 'python-aiohttp-jinja2')
optdepends=('python-aioauth-client: OAuth2 authorization support'
'python-aiohttp-security: authorization support'
'python-aiohttp-session: authorization support'

View File

@ -2,6 +2,7 @@ post_upgrade() {
local breakpoints=(
2.9.0-1
2.12.0-1
2.16.0-1
)
for v in "${breakpoints[@]}"; do

View File

@ -27,6 +27,7 @@ from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn
from ahriman.core.triggers import TriggerLoader
from ahriman.models.repository_id import RepositoryId
from ahriman.web.web import run_server, setup_server
class Web(Handler):
@ -48,9 +49,6 @@ class Web(Handler):
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
"""
# we are using local import for optional dependencies
from ahriman.web.web import run_server, setup_server
spawner_args = Web.extract_arguments(args, configuration)
spawner = Spawn(args.parser(), list(spawner_args))
spawner.start()

View File

@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from aiohttp.web import Application, View
from collections.abc import Generator
import ahriman.web.views
@ -29,22 +30,19 @@ from ahriman.web.views.base import BaseView
__all__ = ["setup_routes"]
def _dynamic_routes(configuration: Configuration) -> dict[str, type[View]]:
def _dynamic_routes(configuration: Configuration) -> Generator[tuple[str, type[View]], None, None]:
"""
extract dynamic routes based on views
Args:
configuration(Configuration): configuration instance
Returns:
dict[str, type[View]]: map of the route to its view
Yields:
tuple[str, type[View]]: map of the route to its view
"""
routes: dict[str, type[View]] = {}
for view in implementations(ahriman.web.views, BaseView):
view_routes = view.routes(configuration)
routes.update([(route, view) for route in view_routes])
return routes
for route in view.routes(configuration):
yield route, view
def setup_routes(application: Application, configuration: Configuration) -> None:
@ -57,5 +55,5 @@ def setup_routes(application: Application, configuration: Configuration) -> None
"""
application.router.add_static("/static", configuration.getpath("web", "static_path"), follow_symlinks=True)
for route, view in _dynamic_routes(configuration).items():
for route, view in _dynamic_routes(configuration):
application.router.add_view(route, view)

View File

@ -28,7 +28,7 @@ from pathlib import Path
prefix = Path(sys.prefix).relative_to("/")
site_packages = Path(site.getsitepackages()[0]).relative_to("/")
SUBPACKAGES = {
"ahriman": [
"ahriman-core": [
prefix / "bin",
prefix / "lib" / "systemd",
prefix / "share",

View File

@ -1 +1,9 @@
# nothing to test here
from ahriman.application.handlers.triggers import Triggers
from ahriman.application.handlers.triggers_support import TriggersSupport
def test_arguments() -> None:
"""
must define own arguments
"""
assert TriggersSupport.arguments != Triggers.arguments

View File

@ -36,8 +36,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
setup_mock = mocker.patch("ahriman.web.web.setup_server")
run_mock = mocker.patch("ahriman.web.web.run_server")
setup_mock = mocker.patch("ahriman.application.handlers.web.setup_server")
run_mock = mocker.patch("ahriman.application.handlers.web.run_server")
start_mock = mocker.patch("ahriman.core.spawn.Spawn.start")
trigger_mock = mocker.patch("ahriman.core.triggers.TriggerLoader.load")
stop_mock = mocker.patch("ahriman.core.spawn.Spawn.stop")

View File

@ -6,6 +6,15 @@ from ahriman.core.module_loader import _modules, implementations
from ahriman.web.views.base import BaseView
def test_modules() -> None:
"""
must load modules
"""
modules = list(_modules(Path(__file__).parent.parent, "ahriman.web.views"))
assert modules
assert all(not module.ispkg for module in modules)
def test_implementations() -> None:
"""
must load implementations from the package
@ -14,12 +23,3 @@ def test_implementations() -> None:
assert routes
assert all(isinstance(view, type) for view in routes)
assert all(issubclass(view, BaseView) for view in routes)
def test_modules() -> None:
"""
must load modules
"""
modules = list(_modules(Path(__file__).parent.parent, "ahriman.web.views"))
assert modules
assert all(not module.ispkg for module in modules)

View File

@ -17,7 +17,7 @@ def test_dynamic_routes(resource_path_root: Path, configuration: Configuration)
if file.suffix == ".py" and file.name not in ("__init__.py", "base.py", "status_view_guard.py")
]
routes = _dynamic_routes(configuration)
routes = dict(_dynamic_routes(configuration))
assert all(isinstance(view, type) for view in routes.values())
assert len(set(routes.values())) == len(expected_views)

View File

@ -36,7 +36,6 @@ async def test_get_not_found(client_with_auth: TestClient, mocker: MockerFixture
"""
must raise not found if path is invalid
"""
print([route.handler for route in client_with_auth.app.router.routes()])
static_route = next(route for route in client_with_auth.app.router.routes() if route.handler == StaticView)
mocker.patch.object(static_route.handler, "ROUTES", [])
response = await client_with_auth.get("/favicon.ico", allow_redirects=False)