Compare commits

..

1 Commits

Author SHA1 Message Date
b6c365612d docs: add check if docs are updated 2025-06-18 20:40:04 +03:00
18 changed files with 1365 additions and 1426 deletions

6
.bandit-test.yml Normal file
View File

@ -0,0 +1,6 @@
skips:
- B101
- B104
- B105
- B106
- B404

View File

@ -26,16 +26,14 @@ jobs:
- ${{ github.workspace }}:/build - ${{ github.workspace }}:/build
steps: steps:
- run: pacman --noconfirm -Syu base-devel git python-tox
- run: git config --global --add safe.directory *
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: pacman --noconfirm -Syu base-devel git python-tox
- name: Run check and tests - name: Run check and tests
run: tox run: tox
- name: Generate documentation and check if there are untracked changes - name: Generate documentation and check if there are untracked changes
run: | run: tox -e docs
tox -e docs
[ -z "$(git status --porcelain docs/*.rst)" ] - uses: numtide/clean-git-action@v2

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

40
tox.ini
View File

@ -6,14 +6,8 @@ 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
[flags] [mypy]
autopep8 = --max-line-length 120 -aa --in-place flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any
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
@ -39,17 +33,19 @@ setenv =
CFLAGS="-Wno-unterminated-string-initialization" CFLAGS="-Wno-unterminated-string-initialization"
MYPYPATH=src MYPYPATH=src
commands = commands =
autopep8 {[flags]autopep8} --exit-code --jobs 0 --recursive "src/{[tox]project_name}" "tests/{[tox]project_name}" autopep8 --exit-code --max-line-length 120 -aa -i -j 0 -r "src/{[tox]project_name}" "tests/{[tox]project_name}"
pylint {[flags]pylint} "src/{[tox]project_name}" pylint --rcfile=.pylint.toml "src/{[tox]project_name}"
bandit {[flags]bandit} --recursive "src/{[tox]project_name}" bandit -c .bandit.yml -r "src/{[tox]project_name}"
bandit {[flags]bandit} --skip B101,B105,B106 --recursive "tests/{[tox]project_name}" bandit -c .bandit-test.yml -r "tests/{[tox]project_name}"
mypy {[flags]mypy} --install-types --non-interactive --package "{[tox]project_name}" mypy {[mypy]flags} -p "{[tox]project_name}" --install-types --non-interactive
[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 =
@ -59,18 +55,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 {[flags]shtab} --shell bash > package/share/bash-completion/completions/_ahriman' 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 zsh > package/share/zsh/site-functions/_ahriman' bash -c 'shtab --shell zsh --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/zsh/site-functions/_ahriman'
argparse-manpage {[flags]manpage} --module ahriman.application.ahriman --function _parser --output ../package/share/man/man1/ahriman.1 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 {[flags]pydeps} --no-output --show-dot --dot-output {tox_root}{/}docs/_static/architecture.dot src/ahriman 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 # 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 --output-dir docs src sphinx-apidoc -o ../docs .
# 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
@ -81,7 +77,7 @@ deps =
pip_pre = true pip_pre = true
recreate = true recreate = true
commands = commands =
sphinx-build --builder html --write-all --jobs auto --fail-on-warning docs {envtmpdir}{/}html sphinx-build -b html -a -j auto -W docs {envtmpdir}{/}html
[testenv:publish] [testenv:publish]
description = Create and publish release to GitHub description = Create and publish release to GitHub