mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 15:27:17 +00:00
deprecate init/repo-init command
In current workflow you need to run setup to run init (because of repository name), but you need to run init before setup (because of repository tree rights). New solution just add `Repo.init()` method call to setup subcommand after config reload to make sure that repository name has been applied. In addition chown method as well as setuid method for check_output have been added.
This commit is contained in:
parent
63e79ec57a
commit
13121298f5
17
.github/workflows/setup.sh
vendored
17
.github/workflows/setup.sh
vendored
@ -25,25 +25,28 @@ make VERSION=1.0.0 archlinux # well, it does not really matter which version we
|
||||
mv ahriman-*-src.tar.xz package/archlinux
|
||||
chmod +777 package/archlinux # because fuck you that's why
|
||||
cd package/archlinux
|
||||
sudo -u nobody makepkg -cf --skipchecksums --noconfirm
|
||||
sudo -u nobody -- makepkg -cf --skipchecksums --noconfirm
|
||||
pacman --noconfirm -U ahriman-1.0.0-1-any.pkg.tar.zst
|
||||
# create machine-id which is required by build tools
|
||||
systemd-machine-id-setup
|
||||
|
||||
# special thing for the container, because /dev/log interface is not available there
|
||||
sed -i 's/handlers = syslog_handler/handlers = console_handler/g' /etc/ahriman.ini.d/logging.ini
|
||||
sed -i "s/handlers = syslog_handler/handlers = console_handler/g" /etc/ahriman.ini.d/logging.ini
|
||||
# initial setup command as root
|
||||
sudo -u ahriman ahriman -a x86_64 init
|
||||
ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "github" --web-port 8080
|
||||
# enable services
|
||||
systemctl enable ahriman-web@x86_64
|
||||
systemctl enable ahriman@x86_64.timer
|
||||
# run web service (detached)
|
||||
sudo -u ahriman ahriman -a x86_64 web &
|
||||
sudo -u ahriman -- ahriman -a x86_64 web &
|
||||
WEBPID=$!
|
||||
sleep 15s # wait for the web service activation
|
||||
# add the first package
|
||||
# the build itself does not really work in the container because it requires procfs
|
||||
sudo -u ahriman ahriman package-add yay
|
||||
# the build itself does not really work in the container
|
||||
sudo -u ahriman -- ahriman package-add --now yay
|
||||
# check if package was actually installed
|
||||
#test -n "$(find "/var/lib/ahriman/repository/x86_64" -name "yay*pkg*")"
|
||||
# run package check
|
||||
sudo -u ahriman ahriman repo-update
|
||||
sudo -u ahriman -- ahriman repo-update
|
||||
# stop web service lol
|
||||
kill $WEBPID
|
||||
|
@ -20,8 +20,6 @@ fi
|
||||
# create repository root inside the [[mounted]] directory and set correct ownership
|
||||
[ -d "$AHRIMAN_REPOSITORY_ROOT" ] || mkdir "$AHRIMAN_REPOSITORY_ROOT"
|
||||
chown "$AHRIMAN_USER":"$AHRIMAN_USER" "$AHRIMAN_REPOSITORY_ROOT"
|
||||
# run initial setup
|
||||
sudo -u "$AHRIMAN_USER" -- ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" repo-init
|
||||
|
||||
# run built-in setup command
|
||||
AHRIMAN_SETUP_ARGS=("--build-as-user" "$AHRIMAN_USER")
|
||||
|
@ -12,7 +12,6 @@ TL;DR
|
||||
|
||||
```shell
|
||||
yay -S ahriman
|
||||
sudo -u ahriman ahriman -a x86_64 init
|
||||
sudo ahriman -a x86_64 repo-setup --packager "ahriman bot <ahriman@example.com>" --repository "repository"
|
||||
systemctl enable --now ahriman@x86_64.timer
|
||||
```
|
||||
|
@ -5,11 +5,10 @@
|
||||
3. TL;DR
|
||||
|
||||
```shell
|
||||
sudo -u ahriman ahriman -a x86_64 repo-init
|
||||
sudo ahriman -a x86_64 repo-setup ...
|
||||
```
|
||||
|
||||
`repo-init` subcommand is required to create the repository tree with correct rights. `repo-setup` literally does the following steps:
|
||||
`repo-setup` literally does the following steps:
|
||||
|
||||
1. Create `/var/lib/ahriman/.makepkg.conf` with `makepkg.conf` overrides if required (at least you might want to set `PACKAGER`):
|
||||
|
||||
|
@ -85,7 +85,6 @@ def _parser() -> argparse.ArgumentParser:
|
||||
_set_repo_check_parser(subparsers)
|
||||
_set_repo_clean_parser(subparsers)
|
||||
_set_repo_config_parser(subparsers)
|
||||
_set_repo_init_parser(subparsers)
|
||||
_set_repo_rebuild_parser(subparsers)
|
||||
_set_repo_remove_unknown_parser(subparsers)
|
||||
_set_repo_report_parser(subparsers)
|
||||
@ -342,19 +341,6 @@ def _set_repo_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
def _set_repo_init_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for repository init subcommand
|
||||
:param root: subparsers for the commands
|
||||
:return: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("repo-init", aliases=["init"], help="create repository tree",
|
||||
description="create empty repository tree. Optional command for auto architecture support",
|
||||
formatter_class=_formatter)
|
||||
parser.set_defaults(handler=handlers.Init, no_report=True)
|
||||
return parser
|
||||
|
||||
|
||||
def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for repository rebuild subcommand
|
||||
@ -406,7 +392,7 @@ def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
:param root: subparsers for the commands
|
||||
:return: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("repo-setup", aliases=["setup"], help="initial service configuration",
|
||||
parser = root.add_parser("repo-setup", aliases=["init", "repo-init", "setup"], help="initial service configuration",
|
||||
description="create initial service configuration, requires root",
|
||||
epilog="Create _minimal_ configuration for the service according to provided options.",
|
||||
formatter_class=_formatter)
|
||||
|
@ -22,7 +22,6 @@ from ahriman.application.handlers.handler import Handler
|
||||
from ahriman.application.handlers.add import Add
|
||||
from ahriman.application.handlers.clean import Clean
|
||||
from ahriman.application.handlers.dump import Dump
|
||||
from ahriman.application.handlers.init import Init
|
||||
from ahriman.application.handlers.key_import import KeyImport
|
||||
from ahriman.application.handlers.patch import Patch
|
||||
from ahriman.application.handlers.rebuild import Rebuild
|
||||
|
@ -27,7 +27,7 @@ from typing import List, Type
|
||||
|
||||
from ahriman.application.lock import Lock
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import MissingArchitecture, MultipleArchitecture
|
||||
from ahriman.core.exceptions import MissingArchitecture, MultipleArchitectures
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@ -95,7 +95,7 @@ class Handler:
|
||||
# actually we do not have to spawn another process if it is single-process application, do we?
|
||||
if len(architectures) > 1:
|
||||
if not cls.ALLOW_MULTI_ARCHITECTURE_RUN:
|
||||
raise MultipleArchitecture(args.command)
|
||||
raise MultipleArchitectures(args.command)
|
||||
|
||||
with Pool(len(architectures)) as pool:
|
||||
result = pool.starmap(
|
||||
|
@ -1,47 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
|
||||
from typing import Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.handler import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
class Init(Handler):
|
||||
"""
|
||||
repository init handler
|
||||
"""
|
||||
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||
|
||||
@classmethod
|
||||
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
|
||||
configuration: Configuration, no_report: bool, unsafe: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
:param args: command line args
|
||||
:param architecture: repository architecture
|
||||
:param configuration: configuration instance
|
||||
:param no_report: force disable reporting
|
||||
:param unsafe: if set no user check will be performed before path creation
|
||||
"""
|
||||
Application(architecture, configuration, no_report, unsafe).repository.repo.init()
|
@ -55,14 +55,19 @@ class Setup(Handler):
|
||||
:param no_report: force disable reporting
|
||||
:param unsafe: if set no user check will be performed before path creation
|
||||
"""
|
||||
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration.include)
|
||||
configuration.reload()
|
||||
|
||||
application = Application(architecture, configuration, no_report, unsafe)
|
||||
|
||||
Setup.configuration_create_makepkg(args.packager, application.repository.paths)
|
||||
Setup.executable_create(args.build_command, architecture)
|
||||
Setup.configuration_create_devtools(args.build_command, architecture, args.from_configuration,
|
||||
args.no_multilib, args.repository, application.repository.paths)
|
||||
Setup.configuration_create_ahriman(args, architecture, args.repository, configuration.include)
|
||||
Setup.configuration_create_sudo(args.build_command, architecture)
|
||||
|
||||
application.repository.repo.init()
|
||||
|
||||
@staticmethod
|
||||
def build_command(prefix: str, architecture: str) -> Path:
|
||||
"""
|
||||
|
@ -32,6 +32,7 @@ from ahriman.core.exceptions import DuplicateRun
|
||||
from ahriman.core.status.client import Client
|
||||
from ahriman.core.util import check_user
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
class Lock:
|
||||
@ -40,7 +41,7 @@ class Lock:
|
||||
:ivar force: remove lock file on start if any
|
||||
:ivar path: path to lock file if any
|
||||
:ivar reporter: build status reporter instance
|
||||
:ivar root: repository root (i.e. ahriman home)
|
||||
:ivar paths: repository paths instance
|
||||
:ivar unsafe: skip user check
|
||||
"""
|
||||
|
||||
@ -55,7 +56,7 @@ class Lock:
|
||||
self.force = args.force
|
||||
self.unsafe = args.unsafe
|
||||
|
||||
self.root = Path(configuration.get("repository", "root"))
|
||||
self.paths = RepositoryPaths(configuration.getpath("repository", "root"), architecture)
|
||||
self.reporter = Client() if args.no_report else Client.load(configuration)
|
||||
|
||||
def __enter__(self) -> Lock:
|
||||
@ -103,7 +104,7 @@ class Lock:
|
||||
"""
|
||||
check if current user is actually owner of ahriman root
|
||||
"""
|
||||
check_user(self.root, self.unsafe)
|
||||
check_user(self.paths, self.unsafe)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
|
@ -34,6 +34,7 @@ class Repo:
|
||||
:ivar name: repository name
|
||||
:ivar paths: repository paths instance
|
||||
:ivar sign_args: additional args which have to be used to sign repository archive
|
||||
:ivar uid: uid of the repository owner user
|
||||
"""
|
||||
|
||||
_check_output = check_output
|
||||
@ -48,6 +49,7 @@ class Repo:
|
||||
self.logger = logging.getLogger("build_details")
|
||||
self.name = name
|
||||
self.paths = paths
|
||||
self.uid, _ = paths.root_owner
|
||||
self.sign_args = sign_args
|
||||
|
||||
@property
|
||||
@ -66,7 +68,8 @@ class Repo:
|
||||
"repo-add", *self.sign_args, "-R", str(self.repo_path), str(path),
|
||||
exception=BuildFailed(path.name),
|
||||
cwd=self.paths.repository,
|
||||
logger=self.logger)
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
|
||||
def init(self) -> None:
|
||||
"""
|
||||
@ -76,7 +79,8 @@ class Repo:
|
||||
"repo-add", *self.sign_args, str(self.repo_path),
|
||||
exception=None,
|
||||
cwd=self.paths.repository,
|
||||
logger=self.logger)
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
|
||||
def remove(self, package: str, filename: Path) -> None:
|
||||
"""
|
||||
@ -93,4 +97,5 @@ class Repo:
|
||||
"repo-remove", *self.sign_args, str(self.repo_path), package,
|
||||
exception=BuildFailed(package),
|
||||
cwd=self.paths.repository,
|
||||
logger=self.logger)
|
||||
logger=self.logger,
|
||||
user=self.uid)
|
||||
|
@ -38,6 +38,7 @@ class Task:
|
||||
:ivar logger: class logger
|
||||
:ivar package: package definitions
|
||||
:ivar paths: repository paths instance
|
||||
:ivar uid: uid of the repository owner user
|
||||
"""
|
||||
|
||||
_check_output = check_output
|
||||
@ -53,6 +54,7 @@ class Task:
|
||||
self.build_logger = logging.getLogger("build_details")
|
||||
self.package = package
|
||||
self.paths = paths
|
||||
self.uid, _ = paths.root_owner
|
||||
|
||||
self.archbuild_flags = configuration.getlist("build", "archbuild_flags", fallback=[])
|
||||
self.build_command = configuration.get("build", "build_command")
|
||||
@ -74,7 +76,8 @@ class Task:
|
||||
*command,
|
||||
exception=BuildFailed(self.package.base),
|
||||
cwd=self.paths.sources_for(self.package.base),
|
||||
logger=self.build_logger)
|
||||
logger=self.build_logger,
|
||||
user=self.uid)
|
||||
|
||||
# well it is not actually correct, but we can deal with it
|
||||
packages = Task._check_output("makepkg", "--packagelist",
|
||||
|
@ -17,6 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
@ -85,6 +86,20 @@ class InvalidOption(ValueError):
|
||||
ValueError.__init__(self, f"Invalid or unknown option value `{value}`")
|
||||
|
||||
|
||||
class InvalidPath(ValueError):
|
||||
"""
|
||||
exception which will be raised on path which is not belong to root directory
|
||||
"""
|
||||
|
||||
def __init__(self, path: Path, root: Path) -> None:
|
||||
"""
|
||||
default constructor
|
||||
:param path: path which raised an exception
|
||||
:param root: repository root (i.e. ahriman home)
|
||||
"""
|
||||
ValueError.__init__(self, f"Path `{path}` does not belong to repository root `{root}`")
|
||||
|
||||
|
||||
class InvalidPackageInfo(RuntimeError):
|
||||
"""
|
||||
exception which will be raised on package load errors
|
||||
@ -111,7 +126,7 @@ class MissingArchitecture(ValueError):
|
||||
ValueError.__init__(self, f"Architecture required for subcommand {command}, but missing")
|
||||
|
||||
|
||||
class MultipleArchitecture(ValueError):
|
||||
class MultipleArchitectures(ValueError):
|
||||
"""
|
||||
exception which will be raised if multiple architectures are not supported by the handler
|
||||
"""
|
||||
|
@ -62,7 +62,7 @@ class Properties:
|
||||
|
||||
self.paths = RepositoryPaths(configuration.getpath("repository", "root"), architecture)
|
||||
try:
|
||||
check_user(self.paths.root, unsafe)
|
||||
check_user(self.paths, unsafe)
|
||||
self.paths.tree_create()
|
||||
except UnsafeRun:
|
||||
self.logger.warning("root owner differs from the current user, skipping tree creation")
|
||||
|
@ -27,10 +27,11 @@ from pathlib import Path
|
||||
from typing import Any, Dict, Generator, Iterable, Optional, Union
|
||||
|
||||
from ahriman.core.exceptions import InvalidOption, UnsafeRun
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path] = None,
|
||||
input_data: Optional[str] = None, logger: Optional[Logger] = None) -> str:
|
||||
input_data: Optional[str] = None, logger: Optional[Logger] = None, user: Optional[int] = None) -> str:
|
||||
"""
|
||||
subprocess wrapper
|
||||
:param args: command line arguments
|
||||
@ -38,12 +39,15 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
|
||||
:param cwd: current working directory
|
||||
:param input_data: data which will be written to command stdin
|
||||
:param logger: logger to log command result if required
|
||||
:param user: run process as specified user
|
||||
:return: command output
|
||||
"""
|
||||
try:
|
||||
# universal_newlines is required to read input from string
|
||||
# FIXME additional workaround for linter and type check which do not know that user arg is supported
|
||||
# pylint: disable=unexpected-keyword-arg
|
||||
result: str = subprocess.check_output(args, cwd=cwd, input=input_data, stderr=subprocess.STDOUT,
|
||||
universal_newlines=True).strip()
|
||||
universal_newlines=True, user=user).strip() # type: ignore
|
||||
if logger is not None:
|
||||
for line in result.splitlines():
|
||||
logger.debug(line)
|
||||
@ -55,18 +59,18 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
|
||||
raise exception or e
|
||||
|
||||
|
||||
def check_user(root: Path, unsafe: bool) -> None:
|
||||
def check_user(paths: RepositoryPaths, unsafe: bool) -> None:
|
||||
"""
|
||||
check if current user is the owner of the root
|
||||
:param root: root directory (i.e. ahriman home)
|
||||
:param paths: repository paths object
|
||||
:param unsafe: if set no user check will be performed before path creation
|
||||
"""
|
||||
if not root.exists():
|
||||
if not paths.root.exists():
|
||||
return # no directory found, skip check
|
||||
if unsafe:
|
||||
return # unsafe flag is enabled, no check performed
|
||||
current_uid = os.getuid()
|
||||
root_uid = root.stat().st_uid
|
||||
root_uid, _ = paths.root_owner
|
||||
if current_uid != root_uid:
|
||||
raise UnsafeRun(current_uid, root_uid)
|
||||
|
||||
|
@ -19,11 +19,14 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Set, Type
|
||||
from typing import Set, Tuple, Type
|
||||
|
||||
from ahriman.core.exceptions import InvalidPath
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -80,6 +83,13 @@ class RepositoryPaths:
|
||||
"""
|
||||
return self.root / "repository" / self.architecture
|
||||
|
||||
@property
|
||||
def root_owner(self) -> Tuple[int, int]:
|
||||
"""
|
||||
:return: owner user and group of the root directory
|
||||
"""
|
||||
return self.owner(self.root)
|
||||
|
||||
@property
|
||||
def sources(self) -> Path:
|
||||
"""
|
||||
@ -101,6 +111,16 @@ class RepositoryPaths:
|
||||
if path.is_dir()
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def owner(path: Path) -> Tuple[int, int]:
|
||||
"""
|
||||
retrieve owner information by path
|
||||
:param path: path for which extract ids
|
||||
:return: owner user and group ids of the directory
|
||||
"""
|
||||
stat = path.stat()
|
||||
return stat.st_uid, stat.st_gid
|
||||
|
||||
def cache_for(self, package_base: str) -> Path:
|
||||
"""
|
||||
get path to cached PKGBUILD and package sources for the package base
|
||||
@ -109,6 +129,28 @@ class RepositoryPaths:
|
||||
"""
|
||||
return self.cache / package_base
|
||||
|
||||
def chown(self, path: Path) -> None:
|
||||
"""
|
||||
set owner of path recursively (from root) to root owner
|
||||
:param path: path to be chown
|
||||
"""
|
||||
def set_owner(current: Path) -> None:
|
||||
"""
|
||||
set owner to the specified path
|
||||
:param current: path to set
|
||||
"""
|
||||
uid, gid = self.owner(current)
|
||||
if uid == root_uid and gid == root_gid:
|
||||
return
|
||||
os.chown(current, root_uid, root_gid, follow_symlinks=False)
|
||||
|
||||
if self.root not in path.parents:
|
||||
raise InvalidPath(path, self.root)
|
||||
root_uid, root_gid = self.root_owner
|
||||
while path != self.root:
|
||||
set_owner(path)
|
||||
path = path.parent
|
||||
|
||||
def manual_for(self, package_base: str) -> Path:
|
||||
"""
|
||||
get manual path for specific package base
|
||||
@ -158,3 +200,4 @@ class RepositoryPaths:
|
||||
self.repository,
|
||||
self.sources):
|
||||
directory.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
self.chown(directory)
|
||||
|
@ -6,7 +6,7 @@ from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import MissingArchitecture, MultipleArchitecture
|
||||
from ahriman.core.exceptions import MissingArchitecture, MultipleArchitectures
|
||||
|
||||
|
||||
def test_architectures_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
@ -94,7 +94,7 @@ def test_execute_multiple_not_supported(args: argparse.Namespace, mocker: Mocker
|
||||
args.command = "web"
|
||||
mocker.patch.object(Handler, "ALLOW_MULTI_ARCHITECTURE_RUN", False)
|
||||
|
||||
with pytest.raises(MultipleArchitecture):
|
||||
with pytest.raises(MultipleArchitectures):
|
||||
Handler.execute(args)
|
||||
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Init
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.properties.check_user")
|
||||
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
|
||||
|
||||
Init.run(args, "x86_64", configuration, True, False)
|
||||
tree_create_mock.assert_called_once_with()
|
||||
init_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_disallow_auto_architecture_run() -> None:
|
||||
"""
|
||||
must not allow multi architecture run
|
||||
"""
|
||||
assert not Init.ALLOW_AUTO_ARCHITECTURE_RUN
|
@ -40,6 +40,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
|
||||
sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
|
||||
executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
|
||||
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
|
||||
paths = RepositoryPaths(configuration.getpath("repository", "root"), "x86_64")
|
||||
|
||||
Setup.run(args, "x86_64", configuration, True, False)
|
||||
@ -49,6 +50,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
makepkg_configuration_mock.assert_called_once_with(args.packager, paths)
|
||||
sudo_configuration_mock.assert_called_once_with(args.build_command, "x86_64")
|
||||
executable_mock.assert_called_once_with(args.build_command, "x86_64")
|
||||
init_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_build_command(args: argparse.Namespace) -> None:
|
||||
|
@ -288,15 +288,6 @@ def test_subparsers_repo_config(parser: argparse.ArgumentParser) -> None:
|
||||
assert args.unsafe
|
||||
|
||||
|
||||
def test_subparsers_repo_init(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
repo-init command must imply no_report
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "repo-init"])
|
||||
assert args.architecture == ["x86_64"]
|
||||
assert args.no_report
|
||||
|
||||
|
||||
def test_subparsers_repo_rebuild_architecture(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
repo-rebuild command must correctly parse architecture list
|
||||
|
@ -82,7 +82,7 @@ def test_check_user(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
check_user_patch = mocker.patch("ahriman.application.lock.check_user")
|
||||
lock.check_user()
|
||||
check_user_patch.assert_called_once_with(lock.root, False)
|
||||
check_user_patch.assert_called_once_with(lock.paths, False)
|
||||
|
||||
|
||||
def test_check_user_exception(lock: Lock, mocker: MockerFixture) -> None:
|
||||
|
@ -9,6 +9,7 @@ from pytest_mock import MockerFixture
|
||||
from ahriman.core.exceptions import InvalidOption, UnsafeRun
|
||||
from ahriman.core.util import check_output, check_user, filter_json, package_like, pretty_datetime, pretty_size, walk
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_check_output(mocker: MockerFixture) -> None:
|
||||
@ -56,37 +57,37 @@ def test_check_user(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must check user correctly
|
||||
"""
|
||||
cwd = Path.cwd()
|
||||
mocker.patch("os.getuid", return_value=cwd.stat().st_uid)
|
||||
check_user(cwd, False)
|
||||
paths = RepositoryPaths(Path.cwd(), "x86_64")
|
||||
mocker.patch("os.getuid", return_value=paths.root_owner[0])
|
||||
check_user(paths, False)
|
||||
|
||||
|
||||
def test_check_user_no_directory(mocker: MockerFixture) -> None:
|
||||
def test_check_user_no_directory(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not fail in case if no directory found
|
||||
"""
|
||||
mocker.patch("pathlib.Path.exists", return_value=False)
|
||||
check_user(Path.cwd(), False)
|
||||
check_user(repository_paths, False)
|
||||
|
||||
|
||||
def test_check_user_exception(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise exception if user differs
|
||||
"""
|
||||
cwd = Path.cwd()
|
||||
mocker.patch("os.getuid", return_value=cwd.stat().st_uid + 1)
|
||||
paths = RepositoryPaths(Path.cwd(), "x86_64")
|
||||
mocker.patch("os.getuid", return_value=paths.root_owner[0] + 1)
|
||||
|
||||
with pytest.raises(UnsafeRun):
|
||||
check_user(cwd, False)
|
||||
check_user(paths, False)
|
||||
|
||||
|
||||
def test_check_unsafe(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip check if unsafe flag is set
|
||||
"""
|
||||
cwd = Path.cwd()
|
||||
mocker.patch("os.getuid", return_value=cwd.stat().st_uid + 1)
|
||||
check_user(cwd, True)
|
||||
paths = RepositoryPaths(Path.cwd(), "x86_64")
|
||||
mocker.patch("os.getuid", return_value=paths.root_owner[0] + 1)
|
||||
check_user(paths, True)
|
||||
|
||||
|
||||
def test_filter_json(package_ahriman: Package) -> None:
|
||||
|
@ -1,10 +1,36 @@
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Callable, Tuple
|
||||
from unittest import mock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.exceptions import InvalidPath
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def _get_owner(root: Path, same: bool) -> Callable[[Path], Tuple[int, int]]:
|
||||
"""
|
||||
mocker function for owner definition
|
||||
:param root: root directory
|
||||
:param same: if True then returns the same as root directory and different otherwise
|
||||
:return: function which can define ownership
|
||||
"""
|
||||
root_owner = (42, 42)
|
||||
nonroot_owner = (42, 42) if same else (1, 1)
|
||||
return lambda path: root_owner if path == root else nonroot_owner
|
||||
|
||||
|
||||
def test_root_owner(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly define root directory owner
|
||||
"""
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.owner", return_value=(42, 142))
|
||||
assert repository_paths.root_owner == (42, 142)
|
||||
|
||||
|
||||
def test_known_architectures(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must list available directory paths
|
||||
@ -14,6 +40,18 @@ def test_known_architectures(repository_paths: RepositoryPaths, mocker: MockerFi
|
||||
iterdir_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_owner(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly retrieve owner of the path
|
||||
"""
|
||||
stat_mock = MagicMock()
|
||||
stat_mock.st_uid = 42
|
||||
stat_mock.st_gid = 142
|
||||
mocker.patch("pathlib.Path.stat", return_value=stat_mock)
|
||||
|
||||
assert RepositoryPaths.owner(repository_paths.root) == (42, 142)
|
||||
|
||||
|
||||
def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return correct path for cache directory
|
||||
@ -23,6 +61,56 @@ def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package)
|
||||
assert path.parent == repository_paths.cache
|
||||
|
||||
|
||||
def test_chown(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly set owner for the directory
|
||||
"""
|
||||
repository_paths.owner = _get_owner(repository_paths.root, same=False)
|
||||
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
|
||||
chown_mock = mocker.patch("os.chown")
|
||||
|
||||
path = repository_paths.root / "path"
|
||||
repository_paths.chown(path)
|
||||
chown_mock.assert_called_once_with(path, 42, 42, follow_symlinks=False)
|
||||
|
||||
|
||||
def test_chown_parent(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly set owner for the directory including parents
|
||||
"""
|
||||
repository_paths.owner = _get_owner(repository_paths.root, same=False)
|
||||
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
|
||||
chown_mock = mocker.patch("os.chown")
|
||||
|
||||
path = repository_paths.root / "parent" / "path"
|
||||
repository_paths.chown(path)
|
||||
chown_mock.assert_has_calls([
|
||||
mock.call(path, 42, 42, follow_symlinks=False),
|
||||
mock.call(path.parent, 42, 42, follow_symlinks=False)
|
||||
])
|
||||
|
||||
|
||||
def test_chown_skip(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip ownership set in case if it is same as root
|
||||
"""
|
||||
repository_paths.owner = _get_owner(repository_paths.root, same=True)
|
||||
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
|
||||
chown_mock = mocker.patch("os.chown")
|
||||
|
||||
path = repository_paths.root / "path"
|
||||
repository_paths.chown(path)
|
||||
chown_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_chown_invalid_path(repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must raise invalid path exception in case if directory outside the root supplied
|
||||
"""
|
||||
with pytest.raises(InvalidPath):
|
||||
repository_paths.chown(repository_paths.root.parent)
|
||||
|
||||
|
||||
def test_manual_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return correct path for manual directory
|
||||
@ -76,9 +164,17 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
|
||||
for prop in dir(repository_paths)
|
||||
if not prop.startswith("_")
|
||||
and not prop.endswith("_for")
|
||||
and prop not in ("architecture", "known_architectures", "root", "tree_clear", "tree_create")
|
||||
and prop not in ("architecture",
|
||||
"chown",
|
||||
"known_architectures",
|
||||
"owner",
|
||||
"root",
|
||||
"root_owner",
|
||||
"tree_clear",
|
||||
"tree_create")
|
||||
}
|
||||
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
|
||||
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
|
||||
|
||||
repository_paths.tree_create()
|
||||
mkdir_mock.assert_has_calls(
|
||||
@ -86,3 +182,4 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
|
||||
mock.call(mode=0o755, parents=True, exist_ok=True)
|
||||
for _ in paths
|
||||
], any_order=True)
|
||||
chown_mock.assert_has_calls([mock.call(getattr(repository_paths, path)) for path in paths], any_order=True)
|
||||
|
@ -25,7 +25,7 @@ makepkg_flags = --skippgpcheck
|
||||
|
||||
[repository]
|
||||
name = aur-clone
|
||||
root = /var/lib/ahriman
|
||||
root = ../../../
|
||||
|
||||
[sign]
|
||||
target =
|
||||
|
Loading…
Reference in New Issue
Block a user