Compare commits

...
2 Commits
Author SHA1 Message Date
arcanis 52d6b0babc feat: remove makepkg.conf local configuration, use ahriman own
configuration instead

We used .makepkg.conf solely for two features - MAKEFLAGS and PACKAGER,
which could be actually easily implemented by using environment
variables
2026-08-13 14:26:49 +03:00
arcanis 3f101dafa4 feat: read includes from list of directories 2026-08-13 14:26:49 +03:00
12 changed files with 116 additions and 98 deletions
@@ -1,6 +1,6 @@
[settings] [settings]
; Relative path to directory with configuration files overrides. Overrides will be applied in alphabetic order. ; Relative path to directory with configuration files overrides. Overrides will be applied in alphabetic order.
include = ahriman.ini.d include = ahriman.ini.d $HOME/.config/ahriman.ini.d
; Relative path to configuration used by logging package. ; Relative path to configuration used by logging package.
logging = ahriman.ini.d/logging.ini logging = ahriman.ini.d/logging.ini
; Perform database migrations on the application start. Do not touch this option unless you know what you are doing. ; Perform database migrations on the application start. Do not touch this option unless you know what you are doing.
@@ -40,12 +40,16 @@ devtools_wrapper = ahriman-archbuild
;ignore_packages = ;ignore_packages =
; Include debug packages. ; Include debug packages.
;include_debug_packages = yes ;include_debug_packages = yes
; List of additional flags passed to make via environment variable via makepkg command.
;make_flags =
; List of additional flags passed to makechrootpkg command. ; List of additional flags passed to makechrootpkg command.
;makechrootpkg_flags = ;makechrootpkg_flags =
; Minimal age in seconds since the latest AUR package modification before automatic updates are allowed.
;min_age = 0
; List of additional flags passed to makepkg command. ; List of additional flags passed to makepkg command.
makepkg_flags = --nocolor --ignorearch makepkg_flags = --nocolor --ignorearch
; Minimal age in seconds since the latest AUR package modification before automatic updates are allowed.
;min_age = 0
; Default packager identifier to be used for package builds
;packager =
; List of paths to be used for implicit dependency scan. Regular expressions are supported. ; List of paths to be used for implicit dependency scan. Regular expressions are supported.
scan_paths = ^usr/lib(?!/cmake).*$ scan_paths = ^usr/lib(?!/cmake).*$
; List of enabled triggers in the order of calls. ; List of enabled triggers in the order of calls.
@@ -18,9 +18,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import argparse import argparse
import multiprocessing
import os
from pathlib import Path from pathlib import Path
from pwd import getpwuid
from typing import ClassVar from typing import ClassVar
from urllib.parse import quote_plus as url_encode from urllib.parse import quote_plus as url_encode
@@ -30,7 +31,6 @@ from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError from ahriman.core.exceptions import MissingArchitectureError
from ahriman.core.utils import enum_values from ahriman.core.utils import enum_values
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.sign_settings import SignSettings from ahriman.models.sign_settings import SignSettings
from ahriman.models.user import User from ahriman.models.user import User
@@ -69,7 +69,6 @@ class Setup(Handler):
application = Application(repository_id, configuration, report=report) application = Application(repository_id, configuration, report=report)
# basically we create configuration here as root, but it is ok, because those files are only used for reading # basically we create configuration here as root, but it is ok, because those files are only used for reading
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server
Setup.configuration_create_devtools( Setup.configuration_create_devtools(
repository_id, args.from_configuration, args.mirror, args.multilib, repository_server) repository_id, args.from_configuration, args.mirror, args.multilib, repository_server)
@@ -131,8 +130,12 @@ class Setup(Handler):
""" """
configuration = Configuration() configuration = Configuration()
section = Configuration.section_name("build", repository_id.name, repository_id.architecture)
configuration.set_option("repository", "name", repository_id.name) # backward compatibility for docker configuration.set_option("repository", "name", repository_id.name) # backward compatibility for docker
section = Configuration.section_name("build", repository_id.name, repository_id.architecture)
configuration.set_option(section, "packager", args.packager)
if args.makeflags_jobs:
configuration.set_option(section, "make_flags", f"-j{multiprocessing.cpu_count()}")
if args.build_as_user is not None: if args.build_as_user is not None:
configuration.set_option(section, "makechrootpkg_flags", f"-U {args.build_as_user}") configuration.set_option(section, "makechrootpkg_flags", f"-U {args.build_as_user}")
@@ -161,8 +164,9 @@ class Setup(Handler):
if args.generate_salt: if args.generate_salt:
configuration.set_option("auth", "salt", User.generate_password(20)) configuration.set_option("auth", "salt", User.generate_password(20))
(root.include / "00-setup-overrides.ini").unlink(missing_ok=True) # remove old-style configuration include_path = next(path for path in root.include if os.access(path, os.W_OK))
target = root.include / f"00-setup-overrides-{repository_id.id}.ini" (include_path / "00-setup-overrides.ini").unlink(missing_ok=True) # remove old-style configuration
target = include_path / f"00-setup-overrides-{repository_id.id}.ini"
with target.open("w", encoding="utf8") as ahriman_configuration: with target.open("w", encoding="utf8") as ahriman_configuration:
configuration.write(ahriman_configuration) configuration.write(ahriman_configuration)
@@ -216,23 +220,4 @@ class Setup(Handler):
with target.open("w", encoding="utf8") as devtools_configuration: with target.open("w", encoding="utf8") as devtools_configuration:
configuration.write(devtools_configuration) configuration.write(devtools_configuration)
@staticmethod
def configuration_create_makepkg(packager: str, makeflags_jobs: bool, paths: RepositoryPaths) -> None:
"""
create configuration for makepkg
Args:
packager(str): packager identifier (e.g. name, email)
makeflags_jobs(bool): set MAKEFLAGS variable to number of cores
paths(RepositoryPaths): repository paths instance
"""
content = f"PACKAGER='{packager}'\n"
if makeflags_jobs:
content += "MAKEFLAGS=\"-j$(nproc)\"\n"
uid, _ = paths.root_owner
home_dir = Path(getpwuid(uid).pw_dir)
(home_dir / ".makepkg.conf").write_text(content, encoding="utf8")
arguments = [_set_service_setup_parser] arguments = [_set_service_setup_parser]
@@ -39,6 +39,7 @@ class Task(LazyLogging):
archbuild_flags(list[str]): command flags for archbuild command archbuild_flags(list[str]): command flags for archbuild command
build_command(list[str]): build command build_command(list[str]): build command
include_debug_packages(bool): whether to include debug packages or not include_debug_packages(bool): whether to include debug packages or not
make_flags(str): MAKEFLAGS variable for makepkg command
makechrootpkg_flags(list[str]): command flags for makechrootpkg command makechrootpkg_flags(list[str]): command flags for makechrootpkg command
makepkg_flags(list[str]): command flags for makepkg command makepkg_flags(list[str]): command flags for makepkg command
package(Package): package definitions package(Package): package definitions
@@ -65,6 +66,9 @@ class Task(LazyLogging):
self.build_command = configuration.getlist("build", "devtools_wrapper") self.build_command = configuration.getlist("build", "devtools_wrapper")
self._legacy_build_command = configuration.getlist("build", "build_command", fallback=[]) self._legacy_build_command = configuration.getlist("build", "build_command", fallback=[])
self.include_debug_packages = configuration.getboolean("build", "include_debug_packages", fallback=True) self.include_debug_packages = configuration.getboolean("build", "include_debug_packages", fallback=True)
# even though this option is declared as list, there is no need to read it as list,
# because it will be converted back to the string anyway
self.make_flags = configuration.get("build", "make_flags", fallback=None)
self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[]) self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[])
self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[]) self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[])
@@ -127,6 +131,8 @@ class Task(LazyLogging):
for key, value in kwargs.items() for key, value in kwargs.items()
if value is not None if value is not None
} }
if self.make_flags is not None:
environment["MAKEFLAGS"] = self.make_flags
self.logger.info("using environment variables %s", environment) self.logger.info("using environment variables %s", environment)
source_files = list(sources_dir.iterdir()) source_files = list(sources_dir.iterdir())
@@ -109,14 +109,14 @@ class Configuration(configparser.RawConfigParser):
return repository_id.architecture return repository_id.architecture
@property @property
def include(self) -> Path: def include(self) -> list[Path]:
""" """
get full path to include directory get full path to include directory(ies)
Returns: Returns:
Path: path to directory with configuration includes list[Path]: path to directory with configuration includes
""" """
return self.getpath("settings", "include") return self.getpathlist("settings", "include")
@property @property
def logging_path(self) -> Path: def logging_path(self) -> Path:
@@ -325,23 +325,19 @@ class Configuration(configparser.RawConfigParser):
section, key = name.rsplit(":", maxsplit=1) section, key = name.rsplit(":", maxsplit=1)
self.set_option(section, key, value) self.set_option(section, key, value)
def load_includes(self, path: Path | None = None) -> None: def load_includes(self) -> None:
""" """
load configuration includes from specified path load configuration includes from specified path
Args:
path(Path | None, optional): path to directory with include files. If none set, the default path will be
used (Default value = None)
""" """
self.includes = [] # reset state self.includes = [] # reset state
try: try:
path = path or self.include for path in self.include: # pylint: disable=not-an-iterable
for include in sorted(path.glob("*.ini")): for include in sorted(path.glob("*.ini")):
if include == self.logging_path: if include == self.logging_path:
continue # we don't want to load logging explicitly continue # we don't want to load logging explicitly
self.read(include) self.read(include)
self.includes.append(include) self.includes.append(include)
except (FileNotFoundError, configparser.NoOptionError, configparser.NoSectionError): except (FileNotFoundError, configparser.NoOptionError, configparser.NoSectionError):
pass pass
@@ -40,10 +40,13 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"required": True, "required": True,
}, },
"include": { "include": {
"type": "path", "type": "list",
"coerce": "absolute_path", "coerce": "list",
"path_exists": True, "schema": {
"path_type": "dir", "type": "path",
"coerce": "absolute_path",
"path_type": "dir",
},
}, },
"logging": { "logging": {
"type": "path", "type": "path",
@@ -218,6 +221,14 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"type": "boolean", "type": "boolean",
"coerce": "boolean", "coerce": "boolean",
}, },
"make_flags": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"makechrootpkg_flags": { "makechrootpkg_flags": {
"type": "list", "type": "list",
"coerce": "list", "coerce": "list",
@@ -226,18 +237,22 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"empty": False, "empty": False,
}, },
}, },
"makepkg_flags": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"min_age": { "min_age": {
"type": "integer", "type": "integer",
"coerce": "integer", "coerce": "integer",
"min": 0, "min": 0,
}, },
"makepkg_flags": { "packager": {
"type": "list", "type": "string",
"coerce": "list", "empty": False,
"schema": {
"type": "string",
"empty": False,
},
}, },
"scan_paths": { "scan_paths": {
"type": "list", "type": "list",
@@ -190,5 +190,5 @@ class Validator(RootValidator):
{"type": "string"} {"type": "string"}
""" """
fn = getattr(value, f"is_{constraint}") fn = getattr(value, f"is_{constraint}")
if not fn(): if value.exists() and not fn():
self._error(field, f"Path {value} must be type of {constraint}") self._error(field, f"Path {value} must be type of {constraint}")
@@ -73,6 +73,7 @@ class Executor(PackageInfo, Cleaner):
""" """
self.reporter.set_building(package.base) self.reporter.set_building(package.base)
default_packager = self.configuration.get("build", "packager", fallback=None)
task = Task(package, self.configuration, self.repository_id, self.paths) task = Task(package, self.configuration, self.repository_id, self.paths)
patches = self.reporter.package_patches_get(package.base, None) patches = self.reporter.package_patches_get(package.base, None)
commit_sha = task.init(path, patches, local_version) commit_sha = task.init(path, patches, local_version)
@@ -86,7 +87,7 @@ class Executor(PackageInfo, Cleaner):
shutil.copy(artifact, path) shutil.copy(artifact, path)
built.append(path / artifact.name) built.append(path / artifact.name)
else: else:
built = task.build(path, PACKAGER=packager) built = task.build(path, PACKAGER=packager or default_packager)
package.with_packages(built) package.with_packages(built)
for src in built: for src in built:
@@ -1,4 +1,5 @@
import argparse import argparse
import multiprocessing
import pytest import pytest
from pathlib import Path from pathlib import Path
@@ -54,7 +55,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
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")
makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init") init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner") owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
@@ -64,7 +64,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
ahriman_configuration_mock.assert_called_once_with(args, repository_id, configuration) ahriman_configuration_mock.assert_called_once_with(args, repository_id, configuration)
devtools_configuration_mock.assert_called_once_with( devtools_configuration_mock.assert_called_once_with(
repository_id, args.from_configuration, args.mirror, args.multilib, f"file://{repository_paths.repository}") repository_id, args.from_configuration, args.mirror, args.multilib, f"file://{repository_paths.repository}")
makepkg_configuration_mock.assert_called_once_with(args.packager, args.makeflags_jobs, repository_paths)
init_mock.assert_called_once_with() init_mock.assert_called_once_with()
@@ -97,7 +96,6 @@ def test_run_with_server(args: argparse.Namespace, configuration: Configuration,
mocker.patch("ahriman.core.database.SQLite.load", return_value=database) 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.core.alpm.repo.Repo.init") mocker.patch("ahriman.core.alpm.repo.Repo.init")
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")
@@ -122,14 +120,18 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
Setup.configuration_create_ahriman(args, repository_id, configuration) Setup.configuration_create_ahriman(args, repository_id, configuration)
set_option_mock.assert_has_calls([ set_option_mock.assert_has_calls([
MockCall("repository", "name", repository_id.name), MockCall("repository", "name", repository_id.name),
MockCall(Configuration.section_name("build", repository_id.name, repository_id.architecture),
"packager", args.packager),
MockCall(Configuration.section_name("build", repository_id.name, repository_id.architecture),
"make_flags", f"-j{multiprocessing.cpu_count()}"),
MockCall(Configuration.section_name("build", repository_id.name, repository_id.architecture), MockCall(Configuration.section_name("build", repository_id.name, repository_id.architecture),
"makechrootpkg_flags", f"-U {args.build_as_user}"), "makechrootpkg_flags", f"-U {args.build_as_user}"),
MockCall(Configuration.section_name( MockCall(Configuration.section_name("alpm", repository_id.name, repository_id.architecture),
"alpm", repository_id.name, repository_id.architecture), "mirror", args.mirror), "mirror", args.mirror),
MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture), "target", MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture),
" ".join([target.name.lower() for target in args.sign_target])), "target", " ".join([target.name.lower() for target in args.sign_target])),
MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture), "key", MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture),
args.sign_key), "key", args.sign_key),
MockCall("web", "port", str(args.web_port)), MockCall("web", "port", str(args.web_port)),
MockCall("status", "address", f"http://127.0.0.1:{str(args.web_port)}"), MockCall("status", "address", f"http://127.0.0.1:{str(args.web_port)}"),
MockCall("web", "unix_socket", str(args.web_unix_socket)), MockCall("web", "unix_socket", str(args.web_unix_socket)),
@@ -137,7 +139,11 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
MockCall("auth", "salt", pytest.helpers.anyvar(str, strict=True)), MockCall("auth", "salt", pytest.helpers.anyvar(str, strict=True)),
]) ])
write_mock.assert_called_once_with(pytest.helpers.anyvar(int)) write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
remove_mock.assert_called_once_with(configuration.include / "00-setup-overrides.ini", missing_ok=True) remove_mock.assert_called_once_with(
next(
path for path in configuration.include) /
"00-setup-overrides.ini",
missing_ok=True)
def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration, def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration,
@@ -217,20 +223,6 @@ def test_configuration_create_devtools_no_multilib(args: argparse.Namespace, con
write_mock.assert_called_once_with(pytest.helpers.anyvar(int)) write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_configuration_create_makepkg(args: argparse.Namespace, repository_paths: RepositoryPaths,
passwd: Any, mocker: MockerFixture) -> None:
"""
must create makepkg configuration
"""
args = _default_args(args)
mocker.patch("ahriman.application.handlers.setup.getpwuid", return_value=passwd)
write_text_mock = mocker.patch("pathlib.Path.write_text", autospec=True)
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, repository_paths)
write_text_mock.assert_called_once_with(
Path("home") / ".makepkg.conf", pytest.helpers.anyvar(str, True), encoding="utf8")
def test_disallow_multi_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
@@ -93,6 +93,31 @@ def test_build_environment(task_ahriman: Task, mocker: MockerFixture) -> None:
) )
def test_build_makeflags(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must build package with MAKEFLAGS variable if set
"""
local = Path("local")
mocker.patch("pathlib.Path.iterdir", return_value=["file"])
mocker.patch("ahriman.core.build_tools.task.Task._package_archives", return_value=[task_ahriman.package.base])
check_output_mock = mocker.patch("ahriman.core.build_tools.task.check_output")
task_ahriman.make_flags = "-j1"
task_ahriman.build(local)
check_output_mock.assert_called_once_with(
"ahriman-archbuild",
"-r", task_ahriman.repository_id.name, "-a", task_ahriman.repository_id.architecture,
"--", "-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck",
exception=pytest.helpers.anyvar(int),
cwd=local,
logger=task_ahriman.logger,
user=task_ahriman.uid,
environment={"MAKEFLAGS": "-j1"},
)
def test_build_dry_run(task_ahriman: Task, mocker: MockerFixture) -> None: def test_build_dry_run(task_ahriman: Task, mocker: MockerFixture) -> None:
""" """
must run devtools in dry-run mode must run devtools in dry-run mode
@@ -102,7 +127,7 @@ def test_build_dry_run(task_ahriman: Task, mocker: MockerFixture) -> None:
mocker.patch("ahriman.core.build_tools.task.Task._package_archives", return_value=[task_ahriman.package.base]) mocker.patch("ahriman.core.build_tools.task.Task._package_archives", return_value=[task_ahriman.package.base])
check_output_mock = mocker.patch("ahriman.core.build_tools.task.check_output") check_output_mock = mocker.patch("ahriman.core.build_tools.task.check_output")
assert task_ahriman.build(local, dry_run=True) == [task_ahriman.package.base] task_ahriman.build(local, dry_run=True)
check_output_mock.assert_called_once_with( check_output_mock.assert_called_once_with(
"ahriman-archbuild", "ahriman-archbuild",
"-r", task_ahriman.repository_id.name, "-a", task_ahriman.repository_id.architecture, "-r", task_ahriman.repository_id.name, "-a", task_ahriman.repository_id.architecture,
@@ -374,17 +374,16 @@ def test_load_environment(configuration: Configuration) -> None:
assert configuration.get("section:identifier", "key") == "value2" assert configuration.get("section:identifier", "key") == "value2"
def test_load_includes(mocker: MockerFixture) -> None: def test_load_includes(configuration: Configuration, mocker: MockerFixture) -> None:
""" """
must load includes must load includes
""" """
mocker.patch.object(Configuration, "logging_path", Path("logging")) mocker.patch.object(Configuration, "logging_path", Path("logging"))
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read") read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
glob_mock = mocker.patch("pathlib.Path.glob", autospec=True, return_value=[Path("include"), Path("logging")]) glob_mock = mocker.patch("pathlib.Path.glob", autospec=True, return_value=[Path("include"), Path("logging")])
configuration = Configuration()
configuration.load_includes(Path("path")) configuration.load_includes()
glob_mock.assert_called_once_with(Path("path"), "*.ini") glob_mock.assert_called_once_with(configuration.path.absolute().parent, "*.ini")
read_mock.assert_called_once_with(Path("include")) read_mock.assert_called_once_with(Path("include"))
assert configuration.includes == [Path("include")] assert configuration.includes == [Path("include")]
@@ -415,17 +414,6 @@ def test_load_includes_no_section() -> None:
configuration.load_includes() configuration.load_includes()
def test_load_includes_default_path(mocker: MockerFixture) -> None:
"""
must load includes from default path
"""
mocker.patch.object(Configuration, "include", Path("path"))
glob_mock = mocker.patch("pathlib.Path.glob", autospec=True, return_value=[])
Configuration().load_includes()
glob_mock.assert_called_once_with(Path("path"), "*.ini")
def test_merge_sections_missing(configuration: Configuration) -> None: def test_merge_sections_missing(configuration: Configuration) -> None:
""" """
must merge create section if not exists must merge create section if not exists
@@ -134,6 +134,10 @@ def test_validate_path_type(validator: Validator, mocker: MockerFixture) -> None
""" """
error_mock = mocker.patch("ahriman.core.configuration.validator.Validator._error") error_mock = mocker.patch("ahriman.core.configuration.validator.Validator._error")
validator._validate_path_type("file", "field", Path("42"))
mocker.patch("pathlib.Path.exists", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=True) mocker.patch("pathlib.Path.is_file", return_value=True)
validator._validate_path_type("file", "field", Path("1")) validator._validate_path_type("file", "field", Path("1"))
+4 -2
View File
@@ -82,7 +82,7 @@ Base configuration settings.
* ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually. * ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
* ``database`` - path to the application SQLite database, string, required. * ``database`` - path to the application SQLite database, string, required.
* ``include`` - path to directory with configuration files overrides, string, optional. Files will be read in alphabetical order. * ``include`` - path to directories with configuration files overrides, space separated list of strings, optional. Files will be read in alphabetical order.
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference. * ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
``alpm:*`` groups ``alpm:*`` groups
@@ -134,9 +134,11 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
* ``devtools_wrapper`` - path to devtools wrapper, space separated list of strings, required. * ``devtools_wrapper`` - path to devtools wrapper, space separated list of strings, required.
* ``ignore_packages`` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional. * ``ignore_packages`` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional.
* ``include_debug_packages`` - distribute debug packages, boolean, optional, default ``yes``. * ``include_debug_packages`` - distribute debug packages, boolean, optional, default ``yes``.
* ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional. * ``make_flags`` - additional flags passed to ``make`` command via environment variable, space separated list of strings, optional.
* ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional. * ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional.
* ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional.
* ``min_age`` - minimal age in seconds since the latest AUR package modification before automatic updates are allowed, integer, optional, default ``0``. * ``min_age`` - minimal age in seconds since the latest AUR package modification before automatic updates are allowed, integer, optional, default ``0``.
* ``packager`` - default packager identifier in form ``Name Surname <mail@example.com>``, string, optional.
* ``scan_paths`` - paths to be used for implicit dependencies scan, space separated list of strings, optional. If any of those paths is matched against the path, it will be added to the allowed list. * ``scan_paths`` - paths to be used for implicit dependencies scan, space separated list of strings, optional. If any of those paths is matched against the path, it will be added to the allowed list.
* ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of definition. * ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of definition.
* ``triggers_known`` - optional list of ``ahriman.core.triggers.Trigger`` class implementations which are not run automatically and used only for trigger discovery and configuration validation. * ``triggers_known`` - optional list of ``ahriman.core.triggers.Trigger`` class implementations which are not run automatically and used only for trigger discovery and configuration validation.