diff --git a/.github/workflows/run-setup.yml b/.github/workflows/run-setup.yml index 2292dde9..79bcb030 100644 --- a/.github/workflows/run-setup.yml +++ b/.github/workflows/run-setup.yml @@ -11,12 +11,14 @@ jobs: runs-on: ubuntu-latest + container: + image: archlinux:latest + volumes: + - ${{ github.workspace }}:/build + options: --privileged -w /build + steps: - uses: actions/checkout@v2 - name: setup the service in arch linux container - run: | - docker run --privileged \ - -v ${{ github.workspace }}:/build -w /build \ - archlinux:latest \ - .github/workflows/setup.sh + run: .github/workflows/setup.sh diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 875be42e..bfbf56bb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,12 +11,14 @@ jobs: runs-on: ubuntu-latest + container: + image: archlinux:latest + volumes: + - ${{ github.workspace }}:/build + options: -w /build + steps: - uses: actions/checkout@v2 - name: run check and tests in arch linux container - run: | - docker run \ - -v ${{ github.workspace }}:/build -w /build \ - archlinux:latest \ - .github/workflows/tests.sh + run: .github/workflows/tests.sh diff --git a/.github/workflows/setup.sh b/.github/workflows/setup.sh index dd048942..fccb79f9 100755 --- a/.github/workflows/setup.sh +++ b/.github/workflows/setup.sh @@ -10,7 +10,7 @@ pacman --noconfirm -Syu # main dependencies pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-srcinfo sudo # make dependencies -pacman --noconfirm -Sy python-pip +pacman --noconfirm -Sy python-build python-installer python-wheel # optional dependencies # VCS support pacman --noconfirm -Sy breezy darcs mercurial subversion diff --git a/.github/workflows/tests.sh b/.github/workflows/tests.sh index 01af64ee..864a8e0c 100755 --- a/.github/workflows/tests.sh +++ b/.github/workflows/tests.sh @@ -4,13 +4,7 @@ set -ex # install dependencies -pacman --noconfirm -Syu base-devel python-pip - -# install python packages -pip install -e .[web] -pip install -e .[check] -pip install -e .[s3] -pip install -e .[test] +pacman --noconfirm -Syu base-devel python-pip python-tox # run test and check targets make check tests diff --git a/.gitignore b/.gitignore index a4d5b094..2c676d87 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,4 @@ ENV/ .venv/ *.tar.xz +status_cache.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7bf68346..96f51dbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,7 @@ RUN YAY_DIR="$(runuser -u build -- mktemp -d)" && \ cd - && rm -r "$YAY_DIR" ## install package dependencies RUN runuser -u build -- yay --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-srcinfo && \ - runuser -u build -- yay --noconfirm -Sy python-pip && \ + runuser -u build -- yay --noconfirm -Sy python-build python-installer python-wheel && \ runuser -u build -- yay --noconfirm -Sy breezy darcs mercurial python-aioauth-client python-aiohttp \ python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security \ python-aiohttp-session python-boto3 python-cryptography python-jinja \ diff --git a/Makefile b/Makefile index 9956c4ab..2936da0e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ PROJECT := ahriman -FILES := AUTHORS COPYING README.md docs package src setup.cfg setup.py web.png +FILES := AUTHORS COPYING README.md docs package src setup.py tox.ini web.png TARGET_FILES := $(addprefix $(PROJECT)/, $(FILES)) IGNORE_FILES := package/archlinux src/.mypy_cache @@ -26,11 +26,8 @@ archive_directory: $(TARGET_FILES) archlinux: archive sed -i "s/pkgver=[0-9.]*/pkgver=$(VERSION)/" package/archlinux/PKGBUILD -check: clean mypy - autopep8 --exit-code --max-line-length 120 -aa -i -j 0 -r "src/$(PROJECT)" "tests/$(PROJECT)" - pylint --rcfile=.pylintrc "src/$(PROJECT)" - bandit -c .bandit.yml -r "src/$(PROJECT)" - bandit -c .bandit-test.yml -r "tests/$(PROJECT)" +check: clean + tox -e check clean: find . -type f -name "$(PROJECT)-*-src.tar.xz" -delete @@ -42,10 +39,6 @@ directory: clean man: cd src && PYTHONPATH=. argparse-manpage --module ahriman.application.ahriman --function _parser --author "ahriman team" --project-name ahriman --author-email "" --url https://github.com/arcan1s/ahriman --output ../docs/ahriman.1 -mypy: - cd src && mypy --implicit-reexport --strict -p "$(PROJECT)" --install-types --non-interactive || true - cd src && mypy --implicit-reexport --strict -p "$(PROJECT)" - push: architecture man archlinux git add package/archlinux/PKGBUILD src/ahriman/version.py docs/ahriman-architecture.svg docs/ahriman.1 git commit -m "Release $(VERSION)" @@ -54,7 +47,7 @@ push: architecture man archlinux git push --tags tests: clean - python setup.py test + tox -e tests version: ifndef VERSION diff --git a/README.md b/README.md index 028c049e..c7561b0e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![tests status](https://github.com/arcan1s/ahriman/actions/workflows/run-tests.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/run-tests.yml) [![setup status](https://github.com/arcan1s/ahriman/actions/workflows/run-setup.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/run-setup.yml) +[![docker image](https://github.com/arcan1s/ahriman/actions/workflows/docker-image.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/docker-image.yml) [![CodeFactor](https://www.codefactor.io/repository/github/arcan1s/ahriman/badge)](https://www.codefactor.io/repository/github/arcan1s/ahriman) Wrapper for managing custom repository inspired by [repo-scripts](https://github.com/arcan1s/repo-scripts). diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 64aa8934..516bc7fd 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -8,7 +8,7 @@ arch=('any') url="https://github.com/arcan1s/ahriman" license=('GPL3') depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo') -makedepends=('python-pip') +makedepends=('python-build' 'python-installer' 'python-wheel') optdepends=('breezy: -bzr packages support' 'darcs: -darcs packages support' 'mercurial: -hg packages support' @@ -32,18 +32,23 @@ backup=('etc/ahriman.ini' build() { cd "$pkgname" - python setup.py build + python -m build --wheel --no-isolation } package() { cd "$pkgname" - python setup.py install --root="$pkgdir" + python -m installer --destdir="$pkgdir" dist/*.whl + + # python-installer actually thinks that you cannot just copy files to root + # thus we need to copy them manually + install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini" + install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini.d/logging.ini" "$pkgdir/etc/ahriman.ini.d/logging.ini" install -Dm644 "$srcdir/$pkgname.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgname.conf" install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf" } -sha512sums=('6ab741bfb42f92ab00d1b6ecfc44426c00e5c433486e014efbdb585715d9a12dbbafc280e5a9f85b941c8681b13a9dad41327a3e3c44a9683ae30c1d6f017f50' - '13718afec2c6786a18f0b223ef8e58dccf0688bca4cdbe203f14071f5031ed20120eb0ce38b52c76cfd6e8b6581a9c9eaa2743eb11abbaca637451a84c33f075' - '55b20f6da3d66e7bbf2add5d95a3b60632df121717d25a993e56e737d14f51fe063eb6f1b38bd81cc32e05db01c0c1d80aaa720c45cde87f238d8b46cdb8cbc4') +sha512sums=('112b0d8aac68e5330bbdd2b86a59c8a9af8ab7a7c636489623c8460bb90f1318585851edd2a97a8ce20e2d2ad93b847b522685df707c190aa39d23ab908fa8ef' + '53d37efec812afebf86281716259f9ea78a307b83897166c72777251c3eebcb587ecee375d907514781fb2a5c808cbb24ef9f3f244f12740155d0603bf213131' + '62b2eccc352d33853ef243c9cddd63663014aa97b87242f1b5bc5099a7dbd69ff3821f24ffc58e1b7f2387bd4e9e9712cc4c67f661b1724ad99cdf09b3717794') diff --git a/package/etc/ahriman.ini b/package/share/ahriman/settings/ahriman.ini similarity index 69% rename from package/etc/ahriman.ini rename to package/share/ahriman/settings/ahriman.ini index 0c1caa34..38dbbbc8 100644 --- a/package/etc/ahriman.ini +++ b/package/share/ahriman/settings/ahriman.ini @@ -1,6 +1,6 @@ [settings] -include = /etc/ahriman.ini.d -logging = /etc/ahriman.ini.d/logging.ini +include = ahriman.ini.d +logging = ahriman.ini.d/logging.ini [alpm] aur_url = https://aur.archlinux.org @@ -36,13 +36,13 @@ target = console use_utf = yes [email] -full_template_path = /usr/share/ahriman/repo-index.jinja2 +full_template_path = /usr/share/ahriman/templates/repo-index.jinja2 no_empty_report = yes -template_path = /usr/share/ahriman/email-index.jinja2 +template_path = /usr/share/ahriman/templates/email-index.jinja2 ssl = disabled [html] -template_path = /usr/share/ahriman/repo-index.jinja2 +template_path = /usr/share/ahriman/templates/repo-index.jinja2 [upload] target = @@ -58,5 +58,5 @@ debug = no debug_check_host = no debug_allowed_hosts = host = 127.0.0.1 -static_path = /usr/share/ahriman/static -templates = /usr/share/ahriman \ No newline at end of file +static_path = /usr/share/ahriman/templates/static +templates = /usr/share/ahriman/templates \ No newline at end of file diff --git a/package/etc/ahriman.ini.d/logging.ini b/package/share/ahriman/settings/ahriman.ini.d/logging.ini similarity index 100% rename from package/etc/ahriman.ini.d/logging.ini rename to package/share/ahriman/settings/ahriman.ini.d/logging.ini diff --git a/package/share/ahriman/build-status.jinja2 b/package/share/ahriman/templates/build-status.jinja2 similarity index 100% rename from package/share/ahriman/build-status.jinja2 rename to package/share/ahriman/templates/build-status.jinja2 diff --git a/package/share/ahriman/build-status/login-modal.jinja2 b/package/share/ahriman/templates/build-status/login-modal.jinja2 similarity index 100% rename from package/share/ahriman/build-status/login-modal.jinja2 rename to package/share/ahriman/templates/build-status/login-modal.jinja2 diff --git a/package/share/ahriman/build-status/package-actions-modals.jinja2 b/package/share/ahriman/templates/build-status/package-actions-modals.jinja2 similarity index 100% rename from package/share/ahriman/build-status/package-actions-modals.jinja2 rename to package/share/ahriman/templates/build-status/package-actions-modals.jinja2 diff --git a/package/share/ahriman/build-status/package-actions-script.jinja2 b/package/share/ahriman/templates/build-status/package-actions-script.jinja2 similarity index 100% rename from package/share/ahriman/build-status/package-actions-script.jinja2 rename to package/share/ahriman/templates/build-status/package-actions-script.jinja2 diff --git a/package/share/ahriman/email-index.jinja2 b/package/share/ahriman/templates/email-index.jinja2 similarity index 100% rename from package/share/ahriman/email-index.jinja2 rename to package/share/ahriman/templates/email-index.jinja2 diff --git a/package/share/ahriman/repo-index.jinja2 b/package/share/ahriman/templates/repo-index.jinja2 similarity index 100% rename from package/share/ahriman/repo-index.jinja2 rename to package/share/ahriman/templates/repo-index.jinja2 diff --git a/package/share/ahriman/static/favicon.ico b/package/share/ahriman/templates/static/favicon.ico similarity index 100% rename from package/share/ahriman/static/favicon.ico rename to package/share/ahriman/templates/static/favicon.ico diff --git a/package/share/ahriman/utils/bootstrap-scripts.jinja2 b/package/share/ahriman/templates/utils/bootstrap-scripts.jinja2 similarity index 100% rename from package/share/ahriman/utils/bootstrap-scripts.jinja2 rename to package/share/ahriman/templates/utils/bootstrap-scripts.jinja2 diff --git a/package/share/ahriman/utils/style.jinja2 b/package/share/ahriman/templates/utils/style.jinja2 similarity index 100% rename from package/share/ahriman/utils/style.jinja2 rename to package/share/ahriman/templates/utils/style.jinja2 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 5148328a..00000000 --- a/setup.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[aliases] -test = pytest - -[tool:pytest] -addopts = --cov=ahriman --cov-report term-missing:skip-covered --spec -asyncio_mode = legacy -spec_test_format = {result} {docstring_summary} diff --git a/setup.py b/setup.py index 19f931aa..6d5620fa 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ setup( "srcinfo", ], setup_requires=[ - "pytest-runner", ], tests_require=[ "pytest", @@ -53,33 +52,33 @@ setup( "package/bin/ahriman", ], data_files=[ - ("/etc", [ - "package/etc/ahriman.ini", + ("share/ahriman/settings", [ + "package/share/ahriman/settings/ahriman.ini", ]), - ("/etc/ahriman.ini.d", [ - "package/etc/ahriman.ini.d/logging.ini", + ("share/ahriman/settings/ahriman.ini.d", [ + "package/share/ahriman/settings/ahriman.ini.d/logging.ini", ]), ("lib/systemd/system", [ "package/lib/systemd/system/ahriman@.service", "package/lib/systemd/system/ahriman@.timer", "package/lib/systemd/system/ahriman-web@.service", ]), - ("share/ahriman", [ - "package/share/ahriman/build-status.jinja2", - "package/share/ahriman/email-index.jinja2", - "package/share/ahriman/repo-index.jinja2", + ("share/ahriman/templates", [ + "package/share/ahriman/templates/build-status.jinja2", + "package/share/ahriman/templates/email-index.jinja2", + "package/share/ahriman/templates/repo-index.jinja2", ]), - ("share/ahriman/build-status", [ - "package/share/ahriman/build-status/login-modal.jinja2", - "package/share/ahriman/build-status/package-actions-modals.jinja2", - "package/share/ahriman/build-status/package-actions-script.jinja2", + ("share/ahriman/templates/build-status", [ + "package/share/ahriman/templates/build-status/login-modal.jinja2", + "package/share/ahriman/templates/build-status/package-actions-modals.jinja2", + "package/share/ahriman/templates/build-status/package-actions-script.jinja2", ]), - ("share/ahriman/static", [ - "package/share/ahriman/static/favicon.ico", + ("share/ahriman/templates/static", [ + "package/share/ahriman/templates/static/favicon.ico", ]), - ("share/ahriman/utils", [ - "package/share/ahriman/utils/bootstrap-scripts.jinja2", - "package/share/ahriman/utils/style.jinja2", + ("share/ahriman/templates/utils", [ + "package/share/ahriman/templates/utils/bootstrap-scripts.jinja2", + "package/share/ahriman/templates/utils/style.jinja2", ]), ("share/man/man1", [ "docs/ahriman.1", @@ -96,7 +95,7 @@ setup( "s3": [ "boto3", ], - "test": [ + "tests": [ "pytest", "pytest-aiohttp", "pytest-cov", diff --git a/src/ahriman/core/configuration.py b/src/ahriman/core/configuration.py index 6c03c17c..2b212632 100644 --- a/src/ahriman/core/configuration.py +++ b/src/ahriman/core/configuration.py @@ -21,6 +21,7 @@ from __future__ import annotations import configparser import logging +import sys from logging.config import fileConfig from pathlib import Path @@ -37,12 +38,14 @@ class Configuration(configparser.RawConfigParser): :cvar ARCHITECTURE_SPECIFIC_SECTIONS: known sections which can be architecture specific (required by dump) :cvar DEFAULT_LOG_FORMAT: default log format (in case of fallback) :cvar DEFAULT_LOG_LEVEL: default log level (in case of fallback) + :cvar SYSTEM_CONFIGURATION_PATH: default system configuration path distributed by package """ DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s" DEFAULT_LOG_LEVEL = logging.DEBUG ARCHITECTURE_SPECIFIC_SECTIONS = ["build", "sign", "web"] + SYSTEM_CONFIGURATION_PATH = Path(sys.prefix) / "share" / "ahriman" / "settings" / "ahriman.ini" def __init__(self) -> None: """ @@ -172,6 +175,8 @@ class Configuration(configparser.RawConfigParser): fully load configuration :param path: path to root configuration file """ + if not path.is_file(): # fallback to the system file + path = self.SYSTEM_CONFIGURATION_PATH self.path = path self.read(self.path) self.load_includes() diff --git a/tests/ahriman/application/handlers/test_handler_user.py b/tests/ahriman/application/handlers/test_handler_user.py index 4b566e44..84500495 100644 --- a/tests/ahriman/application/handlers/test_handler_user.py +++ b/tests/ahriman/application/handlers/test_handler_user.py @@ -131,7 +131,7 @@ def test_configuration_create_with_plain_password( assert generated.check_credentials(service.password, configuration.get("auth", "salt")) -def test_configuration_get_exists(mocker: MockerFixture) -> None: +def test_configuration_get(mocker: MockerFixture) -> None: """ must load configuration from filesystem """ @@ -143,18 +143,6 @@ def test_configuration_get_exists(mocker: MockerFixture) -> None: read_mock.assert_called_once_with(Path("path") / "auth.ini") -def test_configuration_get_not_exists(mocker: MockerFixture) -> None: - """ - must try to load configuration from filesystem - """ - mocker.patch("pathlib.Path.open") - mocker.patch("pathlib.Path.is_file", return_value=False) - read_mock = mocker.patch("ahriman.core.configuration.Configuration.read") - - assert User.configuration_get(Path("path")) - read_mock.assert_called_once_with(Path("path") / "auth.ini") - - def test_configuration_write(configuration: Configuration, mocker: MockerFixture) -> None: """ must write configuration diff --git a/tests/ahriman/core/test_configuration.py b/tests/ahriman/core/test_configuration.py index c0b7f279..2a1a95f6 100644 --- a/tests/ahriman/core/test_configuration.py +++ b/tests/ahriman/core/test_configuration.py @@ -14,6 +14,7 @@ def test_from_path(mocker: MockerFixture) -> None: """ must load configuration """ + mocker.patch("pathlib.Path.is_file", return_value=True) read_mock = mocker.patch("ahriman.core.configuration.Configuration.read") load_includes_mock = mocker.patch("ahriman.core.configuration.Configuration.load_includes") load_logging_mock = mocker.patch("ahriman.core.configuration.Configuration.load_logging") @@ -26,6 +27,19 @@ def test_from_path(mocker: MockerFixture) -> None: load_logging_mock.assert_called_once_with(True) +def test_from_path_file_missing(mocker: MockerFixture) -> None: + """ + must load configuration based on package files + """ + mocker.patch("pathlib.Path.is_file", return_value=False) + mocker.patch("ahriman.core.configuration.Configuration.load_includes") + mocker.patch("ahriman.core.configuration.Configuration.load_logging") + read_mock = mocker.patch("ahriman.core.configuration.Configuration.read") + + configuration = Configuration.from_path(Path("path"), "x86_64", True) + read_mock.assert_called_once_with(configuration.SYSTEM_CONFIGURATION_PATH) + + def test_dump(configuration: Configuration) -> None: """ dump must not be empty diff --git a/tests/testresources/web/templates b/tests/testresources/web/templates index d2954d31..ef7687d2 120000 --- a/tests/testresources/web/templates +++ b/tests/testresources/web/templates @@ -1 +1 @@ -../../../package/share/ahriman \ No newline at end of file +../../../package/share/ahriman/templates \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..4fd08414 --- /dev/null +++ b/tox.ini @@ -0,0 +1,35 @@ +[tox] +envlist = check, tests +dependencies = -e .[s3,web] +project_name = ahriman + +[pytest] +addopts = --cov=ahriman --cov-report=term-missing:skip-covered --spec +asyncio_mode = legacy +spec_test_format = {result} {docstring_summary} + +[testenv] +deps = + -e .[s3,web] + +[testenv:check] +deps = + {[tox]dependencies} + -e .[check] +allowlist_externals = + /bin/bash +setenv = + 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}" + /bin/bash -c 'mypy --implicit-reexport --strict -p "{[tox]project_name}" --install-types --non-interactive || mypy --implicit-reexport --strict -p "{[tox]project_name}"' + +[testenv:tests] +deps = + {[tox]dependencies} + -e .[tests] +commands = + pytest