mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-01-13 17:53:42 +00:00
Compare commits
15 Commits
10b783ab8d
...
feature/tr
| Author | SHA1 | Date | |
|---|---|---|---|
| 29e9ed62d6 | |||
| 0a8d34217f | |||
| 0b69a1bb77 | |||
| ca18ac936d | |||
| 9f25e7a6ef | |||
| 8a1722a1ea | |||
| 0afec3fa6e | |||
| f63b61f413 | |||
| 4574ee7685 | |||
| 19eb0e19e9 | |||
| c366c4289c | |||
| 46af782db2 | |||
| 6443e02352 | |||
| 999ad39d6f | |||
| dfab5f56b2 |
29
docs/ahriman.core.archive.rst
Normal file
29
docs/ahriman.core.archive.rst
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
ahriman.core.archive package
|
||||||
|
============================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.core.archive.archive\_tree module
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.archive.archive_tree
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.archive.archive\_trigger module
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.archive.archive_trigger
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.archive
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@ -8,6 +8,7 @@ Subpackages
|
|||||||
:maxdepth: 4
|
:maxdepth: 4
|
||||||
|
|
||||||
ahriman.core.alpm
|
ahriman.core.alpm
|
||||||
|
ahriman.core.archive
|
||||||
ahriman.core.auth
|
ahriman.core.auth
|
||||||
ahriman.core.build_tools
|
ahriman.core.build_tools
|
||||||
ahriman.core.configuration
|
ahriman.core.configuration
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
# 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/>.
|
||||||
#
|
#
|
||||||
from ahriman.core import context
|
|
||||||
from ahriman.core.archive.archive_tree import ArchiveTree
|
from ahriman.core.archive.archive_tree import ArchiveTree
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.sign.gpg import GPG
|
from ahriman.core.sign.gpg import GPG
|
||||||
@ -45,9 +44,7 @@ class ArchiveTrigger(Trigger):
|
|||||||
Trigger.__init__(self, repository_id, configuration)
|
Trigger.__init__(self, repository_id, configuration)
|
||||||
|
|
||||||
self.paths = configuration.repository_paths
|
self.paths = configuration.repository_paths
|
||||||
|
self.tree = ArchiveTree(self.paths, GPG(configuration).repository_sign_args)
|
||||||
ctx = context.get()
|
|
||||||
self.tree = ArchiveTree(self.paths, ctx.get(GPG).repository_sign_args)
|
|
||||||
|
|
||||||
def on_result(self, result: Result, packages: list[Package]) -> None:
|
def on_result(self, result: Result, packages: list[Package]) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -57,7 +57,7 @@ class ConfigurationMultiDict(dict[str, Any]):
|
|||||||
OptionError: if the key already exists in the dictionary, but not a single value list or a string
|
OptionError: if the key already exists in the dictionary, but not a single value list or a string
|
||||||
"""
|
"""
|
||||||
match self.get(key):
|
match self.get(key):
|
||||||
case [current_value] | str(current_value):
|
case [current_value] | (str() as current_value):
|
||||||
value = f"{current_value} {value}"
|
value = f"{current_value} {value}"
|
||||||
case None:
|
case None:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -36,6 +36,7 @@ class Trigger(LazyLogging):
|
|||||||
CONFIGURATION_SCHEMA(ConfigurationSchema): (class attribute) configuration schema template
|
CONFIGURATION_SCHEMA(ConfigurationSchema): (class attribute) configuration schema template
|
||||||
CONFIGURATION_SCHEMA_FALLBACK(str | None): (class attribute) optional fallback option for defining
|
CONFIGURATION_SCHEMA_FALLBACK(str | None): (class attribute) optional fallback option for defining
|
||||||
configuration schema type used
|
configuration schema type used
|
||||||
|
REQUIRES_REPOSITORY(bool): (class attribute) either trigger requires loaded repository or not
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
repository_id(RepositoryId): repository unique identifier
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ class Trigger(LazyLogging):
|
|||||||
|
|
||||||
CONFIGURATION_SCHEMA: ClassVar[ConfigurationSchema] = {}
|
CONFIGURATION_SCHEMA: ClassVar[ConfigurationSchema] = {}
|
||||||
CONFIGURATION_SCHEMA_FALLBACK: ClassVar[str | None] = None
|
CONFIGURATION_SCHEMA_FALLBACK: ClassVar[str | None] = None
|
||||||
|
REQUIRES_REPOSITORY: ClassVar[bool] = True
|
||||||
|
|
||||||
def __init__(self, repository_id: RepositoryId, configuration: Configuration) -> None:
|
def __init__(self, repository_id: RepositoryId, configuration: Configuration) -> None:
|
||||||
"""
|
"""
|
||||||
@ -79,6 +81,16 @@ class Trigger(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
return self.repository_id.architecture
|
return self.repository_id.architecture
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_allowed_to_run(self) -> bool:
|
||||||
|
"""
|
||||||
|
whether trigger allowed to run or not
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` in case if trigger allowed to run and ``False`` otherwise
|
||||||
|
"""
|
||||||
|
return not (self.REQUIRES_REPOSITORY and self.repository_id.is_empty)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_schema(cls, configuration: Configuration | None) -> ConfigurationSchema:
|
def configuration_schema(cls, configuration: Configuration | None) -> ConfigurationSchema:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -77,8 +77,9 @@ class TriggerLoader(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
instance = cls()
|
instance = cls()
|
||||||
instance.triggers = [
|
instance.triggers = [
|
||||||
instance.load_trigger(trigger, repository_id, configuration)
|
trigger
|
||||||
for trigger in instance.selected_triggers(configuration)
|
for trigger_name in instance.selected_triggers(configuration)
|
||||||
|
if (trigger := instance.load_trigger(trigger_name, repository_id, configuration)).is_allowed_to_run
|
||||||
]
|
]
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@ -57,7 +57,7 @@ def test_repo_init(repo: Repo, mocker: MockerFixture) -> None:
|
|||||||
assert check_output_mock.call_args[0][0] == "repo-add"
|
assert check_output_mock.call_args[0][0] == "repo-add"
|
||||||
|
|
||||||
|
|
||||||
def test_repo_remove(repo: Repo, package_ahriman: Package,mocker: MockerFixture) -> None:
|
def test_repo_remove(repo: Repo, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must call repo-remove on package removal
|
must call repo-remove on package removal
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pytest_mock import MockerFixture
|
|
||||||
|
|
||||||
from ahriman.core.archive import ArchiveTrigger
|
from ahriman.core.archive import ArchiveTrigger
|
||||||
from ahriman.core.archive.archive_tree import ArchiveTree
|
from ahriman.core.archive.archive_tree import ArchiveTree
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.sign.gpg import GPG
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -23,18 +20,15 @@ def archive_tree(configuration: Configuration) -> ArchiveTree:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def archive_trigger(configuration: Configuration, gpg: GPG, mocker: MockerFixture) -> ArchiveTrigger:
|
def archive_trigger(configuration: Configuration) -> ArchiveTrigger:
|
||||||
"""
|
"""
|
||||||
archive trigger fixture
|
archive trigger fixture
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
configuration(Configuration): configuration fixture
|
configuration(Configuration): configuration fixture
|
||||||
gpg(GPG): GPG fixture
|
|
||||||
mocker(MockerFixture): mocker object
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ArchiveTrigger: archive trigger test instance
|
ArchiveTrigger: archive trigger test instance
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core._Context.get", return_value=GPG)
|
|
||||||
_, repository_id = configuration.check_loaded()
|
_, repository_id = configuration.check_loaded()
|
||||||
return ArchiveTrigger(repository_id, configuration)
|
return ArchiveTrigger(repository_id, configuration)
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
from dataclasses import replace
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
@ -23,6 +22,7 @@ def test_symlinks_create(archive_tree: ArchiveTree, package_ahriman: Package, pa
|
|||||||
must create symlinks
|
must create symlinks
|
||||||
"""
|
"""
|
||||||
_original_exists = Path.exists
|
_original_exists = Path.exists
|
||||||
|
|
||||||
def exists_mock(path: Path) -> bool:
|
def exists_mock(path: Path) -> bool:
|
||||||
if path.name in (package.filename for package in package_python_schedule.packages.values()):
|
if path.name in (package.filename for package in package_python_schedule.packages.values()):
|
||||||
return True
|
return True
|
||||||
@ -63,6 +63,53 @@ def test_symlinks_create_empty_filename(archive_tree: ArchiveTree, package_ahrim
|
|||||||
symlinks_mock.assert_not_called()
|
symlinks_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_symlinks_fix(archive_tree: ArchiveTree, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must fix broken symlinks
|
||||||
|
"""
|
||||||
|
_original_exists = Path.exists
|
||||||
|
|
||||||
|
def exists_mock(path: Path) -> bool:
|
||||||
|
if path.name == "symlink":
|
||||||
|
return True
|
||||||
|
return _original_exists(path)
|
||||||
|
|
||||||
|
mocker.patch("pathlib.Path.is_symlink", side_effect=[True, True, False])
|
||||||
|
mocker.patch("pathlib.Path.exists", autospec=True, side_effect=exists_mock)
|
||||||
|
walk_mock = mocker.patch("ahriman.core.archive.archive_tree.walk", return_value=[
|
||||||
|
archive_tree.repository_for() / filename
|
||||||
|
for filename in ("symlink", "broken_symlink", "file")
|
||||||
|
])
|
||||||
|
remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||||
|
|
||||||
|
archive_tree.symlinks_fix()
|
||||||
|
walk_mock.assert_called_once_with(archive_tree.paths.archive / "repos")
|
||||||
|
remove_mock.assert_called_once_with(None, archive_tree.repository_for() / "broken_symlink")
|
||||||
|
|
||||||
|
|
||||||
|
def test_symlinks_fix_foreign_repository(archive_tree: ArchiveTree, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must skip symlinks check if repository name or architecture doesn't match
|
||||||
|
"""
|
||||||
|
_original_exists = Path.exists
|
||||||
|
|
||||||
|
def exists_mock(path: Path) -> bool:
|
||||||
|
if path.name == "symlink":
|
||||||
|
return True
|
||||||
|
return _original_exists(path)
|
||||||
|
|
||||||
|
mocker.patch("pathlib.Path.is_symlink", side_effect=[True, True, False])
|
||||||
|
mocker.patch("pathlib.Path.exists", autospec=True, side_effect=exists_mock)
|
||||||
|
mocker.patch("ahriman.core.archive.archive_tree.walk", return_value=[
|
||||||
|
archive_tree.repository_for().with_name("i686") / filename
|
||||||
|
for filename in ("symlink", "broken_symlink", "file")
|
||||||
|
])
|
||||||
|
remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||||
|
|
||||||
|
archive_tree.symlinks_fix()
|
||||||
|
remove_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_tree_create(archive_tree: ArchiveTree, mocker: MockerFixture) -> None:
|
def test_tree_create(archive_tree: ArchiveTree, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must create repository root if not exists
|
must create repository root if not exists
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from unittest.mock import MagicMock
|
|||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.report import ReportTrigger
|
from ahriman.core.report import ReportTrigger
|
||||||
from ahriman.core.triggers import Trigger
|
from ahriman.core.triggers import Trigger
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
from ahriman.models.result import Result
|
from ahriman.models.result import Result
|
||||||
|
|
||||||
|
|
||||||
@ -13,6 +14,19 @@ def test_architecture(trigger: Trigger) -> None:
|
|||||||
assert trigger.architecture == trigger.repository_id.architecture
|
assert trigger.architecture == trigger.repository_id.architecture
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_allowed_to_run(trigger: Trigger) -> None:
|
||||||
|
"""
|
||||||
|
must return flag correctly
|
||||||
|
"""
|
||||||
|
assert trigger.is_allowed_to_run
|
||||||
|
|
||||||
|
trigger.repository_id = RepositoryId("", "")
|
||||||
|
assert not trigger.is_allowed_to_run
|
||||||
|
|
||||||
|
trigger.REQUIRES_REPOSITORY = False
|
||||||
|
assert trigger.is_allowed_to_run
|
||||||
|
|
||||||
|
|
||||||
def test_configuration_schema(configuration: Configuration) -> None:
|
def test_configuration_schema(configuration: Configuration) -> None:
|
||||||
"""
|
"""
|
||||||
must return used configuration schema
|
must return used configuration schema
|
||||||
|
|||||||
14
tox.toml
14
tox.toml
@ -140,8 +140,6 @@ dynamic_version = "{[project]name}.__version__"
|
|||||||
extras = [
|
extras = [
|
||||||
{ replace = "ref", of = ["project", "extras"], extend = true },
|
{ replace = "ref", of = ["project", "extras"], extend = true },
|
||||||
]
|
]
|
||||||
# TODO: steamline shlex usage after https://github.com/iterative/shtab/pull/192 merge
|
|
||||||
handle_redirect = true
|
|
||||||
pip_pre = true
|
pip_pre = true
|
||||||
set_env.PYTHONPATH = "src"
|
set_env.PYTHONPATH = "src"
|
||||||
set_env.SPHINX_APIDOC_OPTIONS = "members,no-undoc-members,show-inheritance"
|
set_env.SPHINX_APIDOC_OPTIONS = "members,no-undoc-members,show-inheritance"
|
||||||
@ -149,18 +147,14 @@ commands = [
|
|||||||
[
|
[
|
||||||
"shtab",
|
"shtab",
|
||||||
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
||||||
"--shell",
|
"--shell", "bash",
|
||||||
"bash",
|
"--output", "package/share/bash-completion/completions/_ahriman",
|
||||||
">",
|
|
||||||
"package/share/bash-completion/completions/_ahriman",
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"shtab",
|
"shtab",
|
||||||
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
{ replace = "ref", of = ["flags", "shtab"], extend = true },
|
||||||
"--shell",
|
"--shell", "zsh",
|
||||||
"zsh",
|
"--output", "package/share/zsh/site-functions/_ahriman",
|
||||||
">",
|
|
||||||
"package/share/zsh/site-functions/_ahriman",
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"argparse-manpage",
|
"argparse-manpage",
|
||||||
|
|||||||
39
toxfile.py
39
toxfile.py
@ -18,11 +18,9 @@
|
|||||||
# 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 importlib
|
import importlib
|
||||||
import shlex
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from tox.config.sets import EnvConfigSet
|
from tox.config.sets import EnvConfigSet
|
||||||
from tox.config.types import Command
|
|
||||||
from tox.plugin import impl
|
from tox.plugin import impl
|
||||||
from tox.session.state import State
|
from tox.session.state import State
|
||||||
from tox.tox_env.api import ToxEnv
|
from tox.tox_env.api import ToxEnv
|
||||||
@ -56,35 +54,6 @@ def _extract_version(env_conf: EnvConfigSet, python_path: str | None = None) ->
|
|||||||
return {"VERSION": version}
|
return {"VERSION": version}
|
||||||
|
|
||||||
|
|
||||||
def _wrap_commands(env_conf: EnvConfigSet, shell: str = "bash") -> None:
|
|
||||||
"""
|
|
||||||
wrap commands into shell if there is redirect
|
|
||||||
|
|
||||||
Args:
|
|
||||||
env_conf(EnvConfigSet): the core configuration object
|
|
||||||
shell(str, optional): shell command to use (Default value = "bash")
|
|
||||||
"""
|
|
||||||
if not env_conf["handle_redirect"]:
|
|
||||||
return
|
|
||||||
|
|
||||||
# append shell just in case
|
|
||||||
env_conf["allowlist_externals"].append(shell)
|
|
||||||
|
|
||||||
for command in env_conf["commands"]:
|
|
||||||
if len(command.args) < 3: # command itself, redirect and output
|
|
||||||
continue
|
|
||||||
|
|
||||||
redirect, output = command.args[-2:]
|
|
||||||
if redirect not in (">", "2>", "&>"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
command.args = [
|
|
||||||
shell,
|
|
||||||
"-c",
|
|
||||||
f"{Command(command.args[:-2]).shell} {redirect} {shlex.quote(output)}",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@impl
|
@impl
|
||||||
def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
|
def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
|
||||||
"""
|
"""
|
||||||
@ -103,12 +72,6 @@ def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
|
|||||||
default="",
|
default="",
|
||||||
desc="import path for the version variable",
|
desc="import path for the version variable",
|
||||||
)
|
)
|
||||||
env_conf.add_config(
|
|
||||||
keys=["handle_redirect"],
|
|
||||||
of_type=bool,
|
|
||||||
default=False,
|
|
||||||
desc="wrap commands to handle redirects if any",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@impl
|
@impl
|
||||||
@ -124,5 +87,3 @@ def tox_before_run_commands(tox_env: ToxEnv) -> None:
|
|||||||
|
|
||||||
python_path = set_env.load("PYTHONPATH") if "PYTHONPATH" in set_env else None
|
python_path = set_env.load("PYTHONPATH") if "PYTHONPATH" in set_env else None
|
||||||
set_env.update(_extract_version(env_conf, python_path))
|
set_env.update(_extract_version(env_conf, python_path))
|
||||||
|
|
||||||
_wrap_commands(env_conf)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user