mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-08-29 12:59:55 +00:00
Compare commits
5 Commits
0660c33de3
...
2.17.1
Author | SHA1 | Date | |
---|---|---|---|
a07b20bf50 | |||
ed70897c39 | |||
0423c3e67c | |||
571f62327f | |||
286ff4bcef |
@ -108,9 +108,7 @@ RUN cp "/etc/pacman.d/mirrorlist" "/etc/pacman.d/mirrorlist.orig" && \
|
|||||||
sed -i "s/SigLevel *=.*/SigLevel = Optional/g" "/etc/pacman.conf" && \
|
sed -i "s/SigLevel *=.*/SigLevel = Optional/g" "/etc/pacman.conf" && \
|
||||||
pacman -Sy
|
pacman -Sy
|
||||||
## install package and its optional dependencies
|
## install package and its optional dependencies
|
||||||
RUN pacman -S --noconfirm \
|
RUN pacman -S --noconfirm ahriman
|
||||||
--assume-installed python-aiohttp-apispec=3.0.0 \
|
|
||||||
ahriman
|
|
||||||
RUN pacman -S --noconfirm --asdeps \
|
RUN pacman -S --noconfirm --asdeps \
|
||||||
python-aioauth-client \
|
python-aioauth-client \
|
||||||
python-aiohttp-apispec-git \
|
python-aiohttp-apispec-git \
|
||||||
|
3632
docs/_static/architecture.dot
vendored
3632
docs/_static/architecture.dot
vendored
File diff suppressed because it is too large
Load Diff
@ -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.16.0
|
pkgver=2.17.1
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="ArcH linux ReposItory MANager"
|
pkgdesc="ArcH linux ReposItory MANager"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
@ -64,10 +64,10 @@ _shtab_ahriman_service_key_import_option_strings=('-h' '--help' '--key-server')
|
|||||||
_shtab_ahriman_service_repositories_option_strings=('-h' '--help' '--id-only' '--no-id-only')
|
_shtab_ahriman_service_repositories_option_strings=('-h' '--help' '--id-only' '--no-id-only')
|
||||||
_shtab_ahriman_service_run_option_strings=('-h' '--help')
|
_shtab_ahriman_service_run_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
|
_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
|
||||||
_shtab_ahriman_service_shell_option_strings=('-h' '--help')
|
_shtab_ahriman_service_shell_option_strings=('-h' '--help' '-o' '--output')
|
||||||
_shtab_ahriman_service_tree_migrate_option_strings=('-h' '--help')
|
_shtab_ahriman_service_tree_migrate_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
|
_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
|
||||||
_shtab_ahriman_shell_option_strings=('-h' '--help')
|
_shtab_ahriman_shell_option_strings=('-h' '--help' '-o' '--output')
|
||||||
_shtab_ahriman_sign_option_strings=('-h' '--help')
|
_shtab_ahriman_sign_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
|
_shtab_ahriman_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
|
||||||
_shtab_ahriman_status_update_option_strings=('-h' '--help' '-s' '--status')
|
_shtab_ahriman_status_update_option_strings=('-h' '--help' '-s' '--status')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.TH AHRIMAN "1" "2024\-12\-01" "ahriman" "Generated Python Manual"
|
.TH AHRIMAN "1" "2025\-01\-05" "ahriman" "Generated Python Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ahriman
|
ahriman
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -940,7 +940,7 @@ port of the web service
|
|||||||
path to unix socket used for interprocess communications
|
path to unix socket used for interprocess communications
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman service\-shell'\/\fR
|
.SH COMMAND \fI\,'ahriman service\-shell'\/\fR
|
||||||
usage: ahriman service\-shell [\-h] [code]
|
usage: ahriman service\-shell [\-h] [\-o OUTPUT] [code]
|
||||||
|
|
||||||
drop into python shell
|
drop into python shell
|
||||||
|
|
||||||
@ -948,6 +948,11 @@ drop into python shell
|
|||||||
\fBcode\fR
|
\fBcode\fR
|
||||||
instead of dropping into shell, just execute the specified code
|
instead of dropping into shell, just execute the specified code
|
||||||
|
|
||||||
|
.SH OPTIONS \fI\,'ahriman service\-shell'\/\fR
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR \fI\,OUTPUT\/\fR, \fB\-\-output\fR \fI\,OUTPUT\/\fR
|
||||||
|
output commands and result to the file
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman service\-tree\-migrate'\/\fR
|
.SH COMMAND \fI\,'ahriman service\-tree\-migrate'\/\fR
|
||||||
usage: ahriman service\-tree\-migrate [\-h]
|
usage: ahriman service\-tree\-migrate [\-h]
|
||||||
|
|
||||||
|
@ -583,6 +583,7 @@ _shtab_ahriman_service_setup_options=(
|
|||||||
|
|
||||||
_shtab_ahriman_service_shell_options=(
|
_shtab_ahriman_service_shell_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
|
{-o,--output}"[output commands and result to the file (default\: None)]:output:"
|
||||||
":instead of dropping into shell, just execute the specified code (default\: None):"
|
":instead of dropping into shell, just execute the specified code (default\: None):"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -608,6 +609,7 @@ _shtab_ahriman_setup_options=(
|
|||||||
|
|
||||||
_shtab_ahriman_shell_options=(
|
_shtab_ahriman_shell_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
|
{-o,--output}"[output commands and result to the file (default\: None)]:output:"
|
||||||
":instead of dropping into shell, just execute the specified code (default\: None):"
|
":instead of dropping into shell, just execute the specified code (default\: None):"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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.16.0"
|
__version__ = "2.17.1"
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
# 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 code import InteractiveConsole
|
from code import InteractiveConsole
|
||||||
|
from importlib.util import find_spec
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +27,19 @@ class InteractiveShell(InteractiveConsole):
|
|||||||
wrapper around :class:`code.InteractiveConsole` to pass :func:`interact()` to IPython shell
|
wrapper around :class:`code.InteractiveConsole` to pass :func:`interact()` to IPython shell
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def has_ipython() -> bool:
|
||||||
|
"""
|
||||||
|
check if IPython shell is available
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if IPython shell is available, ``False`` otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return find_spec("IPython.terminal.embed") is not None
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
return False
|
||||||
|
|
||||||
def interact(self, *args: Any, **kwargs: Any) -> None:
|
def interact(self, *args: Any, **kwargs: Any) -> None:
|
||||||
"""
|
"""
|
||||||
pass controller to IPython shell
|
pass controller to IPython shell
|
||||||
@ -34,13 +48,13 @@ class InteractiveShell(InteractiveConsole):
|
|||||||
*args(Any): positional arguments
|
*args(Any): positional arguments
|
||||||
**kwargs(Any): keyword arguments
|
**kwargs(Any): keyword arguments
|
||||||
"""
|
"""
|
||||||
try:
|
if self.has_ipython():
|
||||||
from IPython.terminal.embed import InteractiveShellEmbed
|
from IPython.terminal.embed import InteractiveShellEmbed
|
||||||
|
|
||||||
shell = InteractiveShellEmbed(user_ns=self.locals) # type: ignore[no-untyped-call]
|
shell = InteractiveShellEmbed(user_ns=self.locals) # type: ignore[no-untyped-call]
|
||||||
shell.show_banner() # type: ignore[no-untyped-call]
|
shell.show_banner() # type: ignore[no-untyped-call]
|
||||||
shell.interact() # type: ignore[no-untyped-call]
|
shell.interact() # type: ignore[no-untyped-call]
|
||||||
except ImportError:
|
else:
|
||||||
# fallback to default
|
# fallback to default
|
||||||
import readline # pylint: disable=unused-import
|
import readline # pylint: disable=unused-import
|
||||||
InteractiveConsole.interact(self, *args, **kwargs)
|
InteractiveConsole.interact(self, *args, **kwargs)
|
||||||
|
@ -136,7 +136,7 @@ class PackageArchive:
|
|||||||
dependencies, roots = self.depends_on_paths()
|
dependencies, roots = self.depends_on_paths()
|
||||||
installed_packages = self.installed_packages()
|
installed_packages = self.installed_packages()
|
||||||
# build list of packages, which contains both the package itself and (possible) debug packages
|
# build list of packages, which contains both the package itself and (possible) debug packages
|
||||||
packages = list(self.package.packages) + [f"{package}-debug" for package in self.package.packages]
|
packages = list(self.package.packages) + [f"{self.package.base}-debug"]
|
||||||
|
|
||||||
# build initial map of file path -> packages containing this path
|
# build initial map of file path -> packages containing this path
|
||||||
# in fact, keys will contain all libraries the package linked to and all directories it contains
|
# in fact, keys will contain all libraries the package linked to and all directories it contains
|
||||||
|
@ -62,6 +62,8 @@ class Executor(PackageInfo, Cleaner):
|
|||||||
patches = self.reporter.package_patches_get(package.base, None)
|
patches = self.reporter.package_patches_get(package.base, None)
|
||||||
commit_sha = task.init(local_path, patches, local_version)
|
commit_sha = task.init(local_path, patches, local_version)
|
||||||
built = task.build(local_path, PACKAGER=packager_id)
|
built = task.build(local_path, PACKAGER=packager_id)
|
||||||
|
|
||||||
|
package.with_packages(built, self.pacman)
|
||||||
for src in built:
|
for src in built:
|
||||||
dst = self.paths.packages / src.name
|
dst = self.paths.packages / src.name
|
||||||
shutil.move(src, dst)
|
shutil.move(src, dst)
|
||||||
|
@ -568,3 +568,19 @@ class Package(LazyLogging):
|
|||||||
dict[str, Any]: json-friendly dictionary
|
dict[str, Any]: json-friendly dictionary
|
||||||
"""
|
"""
|
||||||
return dataclass_view(self)
|
return dataclass_view(self)
|
||||||
|
|
||||||
|
def with_packages(self, packages: list[Path], pacman: Pacman) -> None:
|
||||||
|
"""
|
||||||
|
replace packages descriptions with ones from archives
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(Iterable[Path]): paths to package archives
|
||||||
|
pacman(Pacman): alpm wrapper instance
|
||||||
|
"""
|
||||||
|
self.packages = {} # reset state
|
||||||
|
for package in packages:
|
||||||
|
archive = self.from_archive(package, pacman)
|
||||||
|
if archive.base != self.base:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.packages.update(archive.packages)
|
||||||
|
@ -1,14 +1,30 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from ahriman.application.interactive_shell import InteractiveShell
|
from ahriman.application.interactive_shell import InteractiveShell
|
||||||
|
|
||||||
|
|
||||||
|
def test_has_ipython(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must correctly check if IPython is installed
|
||||||
|
"""
|
||||||
|
find_spec_mock = mocker.patch("ahriman.application.interactive_shell.find_spec")
|
||||||
|
assert InteractiveShell.has_ipython()
|
||||||
|
find_spec_mock.assert_called_once_with("IPython.terminal.embed")
|
||||||
|
|
||||||
|
|
||||||
|
def test_has_ipython_module_not_found(mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must return False if IPython is not installed
|
||||||
|
"""
|
||||||
|
mocker.patch("ahriman.application.interactive_shell.find_spec", side_effect=ModuleNotFoundError)
|
||||||
|
assert not InteractiveShell.has_ipython()
|
||||||
|
|
||||||
|
|
||||||
def test_interact(mocker: MockerFixture) -> None:
|
def test_interact(mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must call IPython shell
|
must call IPython shell
|
||||||
"""
|
"""
|
||||||
|
mocker.patch("ahriman.application.interactive_shell.InteractiveShell.has_ipython", return_value=True)
|
||||||
banner_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.show_banner")
|
banner_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.show_banner")
|
||||||
interact_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.interact")
|
interact_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.interact")
|
||||||
|
|
||||||
@ -18,11 +34,11 @@ def test_interact(mocker: MockerFixture) -> None:
|
|||||||
interact_mock.assert_called_once_with()
|
interact_mock.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
def test_interact_import_error(mocker: MockerFixture) -> None:
|
def test_interact_no_ipython(mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must call builtin shell if no IPython available
|
must call builtin shell if no IPython available
|
||||||
"""
|
"""
|
||||||
pytest.helpers.import_error("IPython.terminal.embed", ["InteractiveShellEmbed"], mocker)
|
mocker.patch("ahriman.application.interactive_shell.InteractiveShell.has_ipython", return_value=None)
|
||||||
interact_mock = mocker.patch("code.InteractiveConsole.interact")
|
interact_mock = mocker.patch("code.InteractiveConsole.interact")
|
||||||
|
|
||||||
shell = InteractiveShell()
|
shell = InteractiveShell()
|
||||||
|
@ -29,9 +29,11 @@ def test_process_build(executor: Executor, package_ahriman: Package, passwd: Any
|
|||||||
depends_on_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.depends_on",
|
depends_on_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.depends_on",
|
||||||
return_value=Dependencies())
|
return_value=Dependencies())
|
||||||
dependencies_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_update")
|
dependencies_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_update")
|
||||||
|
with_packages_mock = mocker.patch("ahriman.models.package.Package.with_packages")
|
||||||
|
|
||||||
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=False)
|
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=False)
|
||||||
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
|
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
|
||||||
|
with_packages_mock.assert_called_once_with([Path(package_ahriman.base)], executor.pacman)
|
||||||
changes_mock.assert_called_once_with(package_ahriman.base)
|
changes_mock.assert_called_once_with(package_ahriman.base)
|
||||||
depends_on_mock.assert_called_once_with()
|
depends_on_mock.assert_called_once_with()
|
||||||
dependencies_mock.assert_called_once_with(package_ahriman.base, Dependencies())
|
dependencies_mock.assert_called_once_with(package_ahriman.base, Dependencies())
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import copy
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock, call as MockCall
|
||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -493,3 +495,23 @@ def test_build_status_pretty_print(package_ahriman: Package) -> None:
|
|||||||
"""
|
"""
|
||||||
assert package_ahriman.pretty_print()
|
assert package_ahriman.pretty_print()
|
||||||
assert isinstance(package_ahriman.pretty_print(), str)
|
assert isinstance(package_ahriman.pretty_print(), str)
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_packages(package_ahriman: Package, package_python_schedule: Package, pacman: Pacman,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must correctly replace packages descriptions
|
||||||
|
"""
|
||||||
|
paths = [Path("1"), Path("2")]
|
||||||
|
from_archive_mock = mocker.patch("ahriman.models.package.Package.from_archive", side_effect=[
|
||||||
|
package_ahriman, package_python_schedule
|
||||||
|
])
|
||||||
|
|
||||||
|
result = copy.deepcopy(package_ahriman)
|
||||||
|
package_ahriman.packages[package_ahriman.base].architecture = "i686"
|
||||||
|
|
||||||
|
result.with_packages(paths, pacman)
|
||||||
|
from_archive_mock.assert_has_calls([
|
||||||
|
MockCall(path, pacman) for path in paths
|
||||||
|
])
|
||||||
|
assert result.packages[result.base] == package_ahriman.packages[package_ahriman.base]
|
||||||
|
Reference in New Issue
Block a user