feat: allow to use single web instance for all repositories (#114)

* Allow to use single web instance for any repository

* some improvements

* drop includes from user home directory, introduce new variables to docker

The old solution didn't actually work as expected, because devtools
configuration belongs to filesystem (as well as sudo one), so it was
still required to run setup command.

In order to handle additional repositories, the POSTSETUP and PRESETUP
commands variables have been introduced. FAQ has been updated as well

* raise 404 in case if repository is unknown
This commit is contained in:
2023-10-17 03:53:33 +03:00
parent bf9a46936c
commit 1e00bf9398
141 changed files with 2037 additions and 917 deletions

View File

@@ -30,7 +30,12 @@ def test_call(args: argparse.Namespace, configuration: Configuration, mocker: Mo
assert Handler.call(args, repository_id)
configuration_mock.assert_called_once_with(args.configuration, repository_id)
log_handler_mock.assert_called_once_with(args.log_handler)
log_load_mock.assert_called_once_with(configuration, args.log_handler, quiet=args.quiet, report=args.report)
log_load_mock.assert_called_once_with(
repository_id,
configuration,
args.log_handler,
quiet=args.quiet,
report=args.report)
enter_mock.assert_called_once_with()
exit_mock.assert_called_once_with(None, None, None)
@@ -115,13 +120,24 @@ def test_run(args: argparse.Namespace, configuration: Configuration) -> None:
Handler.run(args, repository_id, configuration, report=True)
def test_check_if_empty() -> None:
"""
must raise exception in case if predicate is True and enabled
"""
Handler.check_if_empty(False, False)
Handler.check_if_empty(True, False)
Handler.check_if_empty(False, True)
with pytest.raises(ExitCode):
Handler.check_if_empty(True, True)
def test_repositories_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must generate list of available repositories based on flags
"""
args.architecture = ["arch"]
args.architecture = "arch"
args.configuration = configuration.path
args.repository = ["repo"]
args.repository = "repo"
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
@@ -135,7 +151,7 @@ def test_repositories_extract_repository(args: argparse.Namespace, configuration
"""
must generate list of available repositories based on flags and tree
"""
args.architecture = ["arch"]
args.architecture = "arch"
args.configuration = configuration.path
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
@@ -151,7 +167,7 @@ def test_repositories_extract_repository_legacy(args: argparse.Namespace, config
"""
must generate list of available repositories based on flags and tree
"""
args.architecture = ["arch"]
args.architecture = "arch"
args.configuration = configuration.path
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
@@ -168,7 +184,7 @@ def test_repositories_extract_architecture(args: argparse.Namespace, configurati
must read repository name from config
"""
args.configuration = configuration.path
args.repository = ["repo"]
args.repository = "repo"
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures",
return_value={"arch"})
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
@@ -207,6 +223,21 @@ def test_repositories_extract_systemd(args: argparse.Namespace, configuration: C
known_repositories_mock.assert_not_called()
def test_repositories_extract_systemd_with_dash(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must extract repository list by using dash separated identifier
"""
args.configuration = configuration.path
args.repository_id = "i686-some-repo-name"
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
assert Handler.repositories_extract(args) == [RepositoryId("i686", "some-repo-name")]
known_architectures_mock.assert_not_called()
known_repositories_mock.assert_not_called()
def test_repositories_extract_systemd_legacy(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
@@ -221,14 +252,3 @@ def test_repositories_extract_systemd_legacy(args: argparse.Namespace, configura
assert Handler.repositories_extract(args) == [RepositoryId("i686", "aur-clone")]
known_architectures_mock.assert_not_called()
known_repositories_mock.assert_called_once_with(configuration.repository_paths.root)
def test_check_if_empty() -> None:
"""
must raise exception in case if predicate is True and enabled
"""
Handler.check_if_empty(False, False)
Handler.check_if_empty(True, False)
Handler.check_if_empty(False, True)
with pytest.raises(ExitCode):
Handler.check_if_empty(True, True)

View File

@@ -1,4 +1,5 @@
import argparse
import pytest
from pytest_mock import MockerFixture
@@ -16,6 +17,9 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.info = False
args.key = None
args.section = None
args.secure = True
return args
@@ -35,6 +39,48 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
print_mock.assert_called()
def test_run_info(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command with info
"""
args = _default_args(args)
args.info = True
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
print_mock.assert_called()
def test_run_section(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command with filter by section
"""
args = _default_args(args)
args.section = "settings"
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=" = ")
def test_run_section_key(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command with filter by section and key
"""
args = _default_args(args)
args.section = "settings"
args.key = "include"
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump")
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called()
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run

View File

@@ -0,0 +1,37 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers import Repositories
from ahriman.core.configuration import Configuration
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.configuration = None # doesn't matter actually
args.id_only = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
application_mock = mocker.patch("ahriman.application.handlers.Handler.repositories_extract",
return_value=[repository_id])
Repositories.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int))
print_mock.assert_called_once_with(verbose=not args.id_only, log_fn=pytest.helpers.anyvar(int), separator=": ")

View File

@@ -44,8 +44,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
aur_search_mock.assert_called_once_with("ahriman", pacman=pytest.helpers.anyvar(int))
official_search_mock.assert_called_once_with("ahriman", pacman=pytest.helpers.anyvar(int))
aur_search_mock.assert_called_once_with("ahriman")
official_search_mock.assert_called_once_with("ahriman")
check_mock.assert_called_once_with(False, False)
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),

View File

@@ -38,7 +38,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)
package_mock.assert_called_once_with(package_ahriman.base, repository.pacman, None)
package_mock.assert_called_once_with(package_ahriman.base, None)
application_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" -> ")
check_mock.assert_called_once_with(args.exit_code, True)

View File

@@ -25,7 +25,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.architecture = ["x86_64"]
args.architecture = "x86_64"
args.build_as_user = "ahriman"
args.from_configuration = Path("/usr/share/devtools/pacman.conf.d/extra.conf")
args.generate_salt = True
@@ -33,7 +33,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.mirror = "mirror"
args.multilib = True
args.packager = "John Doe <john@doe.com>"
args.repository = ["aur-clone"]
args.repository = "aur-clone"
args.server = None
args.sign_key = "key"
args.sign_target = [SignSettings.Packages]
@@ -127,6 +127,7 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
mocker.patch("pathlib.Path.open")
set_option_mock = mocker.patch("ahriman.core.configuration.Configuration.set_option")
write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
remove_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
_, repository_id = configuration.check_loaded()
command = Setup.build_command(repository_paths.root, repository_id)
@@ -143,13 +144,12 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
" ".join([target.name.lower() for target in args.sign_target])),
MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture), "key",
args.sign_key),
MockCall(Configuration.section_name("web", repository_id.name, repository_id.architecture), "port",
str(args.web_port)),
MockCall(Configuration.section_name("web", repository_id.name, repository_id.architecture), "unix_socket",
str(args.web_unix_socket)),
MockCall("web", "port", str(args.web_port)),
MockCall("web", "unix_socket", str(args.web_unix_socket)),
MockCall("auth", "salt", pytest.helpers.anyvar(str, strict=True)),
])
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)
def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration,

View File

@@ -47,13 +47,16 @@ def test_run_packages(args: argparse.Namespace, configuration: Configuration, re
must run command with specified packages
"""
args = _default_args(args)
args.package = [package_ahriman.base]
args.package = [package_ahriman.base, "package"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
add_mock = mocker.patch("ahriman.core.status.client.Client.package_add")
update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
_, repository_id = configuration.check_loaded()
StatusUpdate.run(args, repository_id, configuration, report=False)
update_mock.assert_called_once_with(package_ahriman.base, args.status)
add_mock.assert_called_once_with(package_ahriman, args.status)
update_mock.assert_called_once_with("package", args.status)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,

View File

@@ -20,6 +20,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
argparse.Namespace: generated arguments for these test cases
"""
args.parser = lambda: True
args.configuration = None # doesn't matter actually
args.force = False
args.log_handler = None
args.report = True
@@ -35,15 +36,16 @@ 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_service")
setup_mock = mocker.patch("ahriman.web.web.setup_server")
run_mock = mocker.patch("ahriman.web.web.run_server")
start_mock = mocker.patch("ahriman.core.spawn.Spawn.start")
stop_mock = mocker.patch("ahriman.core.spawn.Spawn.stop")
join_mock = mocker.patch("ahriman.core.spawn.Spawn.join")
_, repository_id = configuration.check_loaded()
mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[repository_id])
Web.run(args, repository_id, configuration, report=False)
setup_mock.assert_called_once_with(repository_id, configuration, pytest.helpers.anyvar(int))
setup_mock.assert_called_once_with(configuration, pytest.helpers.anyvar(int), [repository_id])
run_mock.assert_called_once_with(pytest.helpers.anyvar(int))
start_mock.assert_called_once_with()
stop_mock.assert_called_once_with()
@@ -54,35 +56,32 @@ def test_extract_arguments(args: argparse.Namespace, configuration: Configuratio
"""
must extract correct args
"""
_, repository_id = configuration.check_loaded()
expected = [
"--architecture", repository_id.architecture,
"--repository", repository_id.name,
"--configuration", str(configuration.path),
]
probe = _default_args(args)
assert list(Web.extract_arguments(probe, repository_id, configuration)) == expected
assert list(Web.extract_arguments(probe, configuration)) == expected
probe.force = True
expected.extend(["--force"])
assert list(Web.extract_arguments(probe, repository_id, configuration)) == expected
assert list(Web.extract_arguments(probe, configuration)) == expected
probe.log_handler = LogHandler.Console
expected.extend(["--log-handler", probe.log_handler.value])
assert list(Web.extract_arguments(probe, repository_id, configuration)) == expected
assert list(Web.extract_arguments(probe, configuration)) == expected
probe.quiet = True
expected.extend(["--quiet"])
assert list(Web.extract_arguments(probe, repository_id, configuration)) == expected
assert list(Web.extract_arguments(probe, configuration)) == expected
probe.unsafe = True
expected.extend(["--unsafe"])
assert list(Web.extract_arguments(probe, repository_id, configuration)) == expected
assert list(Web.extract_arguments(probe, configuration)) == expected
configuration.set_option("web", "wait_timeout", "60")
expected.extend(["--wait-timeout", "60"])
assert list(Web.extract_arguments(probe, repository_id, configuration)) == expected
assert list(Web.extract_arguments(probe, configuration)) == expected
def test_extract_arguments_full(parser: argparse.ArgumentParser, configuration: Configuration):
@@ -104,10 +103,7 @@ def test_extract_arguments_full(parser: argparse.ArgumentParser, configuration:
value = action.type(value)
setattr(args, action.dest, value)
_, repository_id = configuration.check_loaded()
assert list(Web.extract_arguments(args, repository_id, configuration)) == [
"--architecture", repository_id.architecture,
"--repository", repository_id.name,
assert list(Web.extract_arguments(args, configuration)) == [
"--configuration", str(configuration.path),
"--force",
"--log-handler", "console",