feat: get rid of sudoers modification during setup command

This commit introduces small shell wrapper, which calls archbuild
mimicing symlinked command. It allows to distribute static sudoers file.

Additionally build_command option has been renamed to devtools_wrapper
in order to allow to keep backward compatibility
This commit is contained in:
2026-08-09 17:37:38 +03:00
parent 9c3d1471e0
commit e182e110d4
18 changed files with 123 additions and 160 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ For installation details kindly refer to the [documentation](https://ahriman.rea
Every available option is described in the [documentation](https://ahriman.readthedocs.io/en/stable/configuration.html). Every available option is described in the [documentation](https://ahriman.readthedocs.io/en/stable/configuration.html).
The application provides reasonable defaults which allow to use it out-of-box; however additional steps (like configuring build toolchain and sudoers) are recommended and can be easily achieved by following install instructions. The application provides reasonable defaults which allow to use it out-of-box; however additional steps (like configuring build toolchain) are recommended and can be easily achieved by following install instructions.
## [FAQ](https://ahriman.readthedocs.io/en/stable/faq/index.html) ## [FAQ](https://ahriman.readthedocs.io/en/stable/faq/index.html)
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
set -euo pipefail
usage() {
local cmd="${0##*/}"
echo "Usage: $cmd [options] -- [archbuild args]"
echo " -r <repository> Repository name"
echo " -a <architecture> Repository architecture"
exit 1
}
repository=
architecture=
while getopts ":r:a:" arg; do
case "$arg" in
r) repository="$OPTARG" ;;
a) architecture="$OPTARG" ;;
*) usage ;;
esac
done
if [[ -z $repository || -z $architecture ]]; then
usage
fi
source "/usr/share/devtools/lib/archroot.sh"
check_root "SOURCE_DATE_EPOCH,SRCDEST,SRCPKGDEST,PKGDEST,LOGDEST,NPROC,MAKEFLAGS,PACKAGER,GNUPGHOME" "${BASH_SOURCE[0]}" "$@"
exec bash -c '
source "$1" "${@:2}"
' "${repository}-${architecture}-build" "archbuild" "${@:$OPTIND}"
@@ -35,7 +35,7 @@ retry_backoff = 1.0
; List of additional flags passed to archbuild command. ; List of additional flags passed to archbuild command.
;archbuild_flags = ;archbuild_flags =
; Path to build command. ; Path to build command.
;build_command = devtools_wrapper = ahriman-archbuild
; List of packages to be ignored during automatic updates. ; List of packages to be ignored during automatic updates.
;ignore_packages = ;ignore_packages =
; Include debug packages. ; Include debug packages.
+1
View File
@@ -57,6 +57,7 @@ packages = [
] ]
[tool.hatch.build.targets.wheel.shared-data] [tool.hatch.build.targets.wheel.shared-data]
"package/bin" = "bin"
"package/lib" = "lib" "package/lib" = "lib"
"package/share" = "share" "package/share" = "share"
@@ -40,16 +40,12 @@ class Setup(Handler):
setup handler setup handler
Attributes: Attributes:
ARCHBUILD_COMMAND_PATH(Path): (class attribute) default devtools command
MIRRORLIST_PATH(Path): (class attribute) path to pacman default mirrorlist (used by multilib repository) MIRRORLIST_PATH(Path): (class attribute) path to pacman default mirrorlist (used by multilib repository)
SUDOERS_DIR_PATH(Path): (class attribute) path to sudoers.d includes directory
""" """
ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
ARCHBUILD_COMMAND_PATH: ClassVar[Path] = Path("/") / "usr" / "bin" / "archbuild"
MIRRORLIST_PATH: ClassVar[Path] = Path("/") / "etc" / "pacman.d" / "mirrorlist" MIRRORLIST_PATH: ClassVar[Path] = Path("/") / "etc" / "pacman.d" / "mirrorlist"
SUDOERS_DIR_PATH: ClassVar[Path] = Path("/") / "etc" / "sudoers.d"
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
@@ -74,11 +70,9 @@ class Setup(Handler):
# 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) Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
Setup.executable_create(application.repository.paths, repository_id)
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)
Setup.configuration_create_sudo(application.repository.paths, repository_id)
# finish initialization # finish initialization
with application.repository.paths.preserve_owner(): with application.repository.paths.preserve_owner():
@@ -124,20 +118,6 @@ class Setup(Handler):
parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True) parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
return parser return parser
@staticmethod
def build_command(root: Path, repository_id: RepositoryId) -> Path:
"""
generate build command name
Args:
root(Path): root directory for the build command (must be root of the repository)
repository_id(RepositoryId): repository unique identifier
Returns:
Path: valid devtools command name
"""
return root / f"{repository_id.name}-{repository_id.architecture}-build"
@staticmethod @staticmethod
def configuration_create_ahriman(args: argparse.Namespace, repository_id: RepositoryId, def configuration_create_ahriman(args: argparse.Namespace, repository_id: RepositoryId,
root: Configuration) -> None: root: Configuration) -> None:
@@ -152,8 +132,6 @@ class Setup(Handler):
configuration = Configuration() configuration = Configuration()
section = Configuration.section_name("build", repository_id.name, repository_id.architecture) section = Configuration.section_name("build", repository_id.name, repository_id.architecture)
build_command = Setup.build_command(root.repository_paths.root, repository_id)
configuration.set_option(section, "build_command", str(build_command))
configuration.set_option("repository", "name", repository_id.name) # backward compatibility for docker configuration.set_option("repository", "name", repository_id.name) # backward compatibility for docker
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}")
@@ -257,31 +235,4 @@ class Setup(Handler):
home_dir = Path(getpwuid(uid).pw_dir) home_dir = Path(getpwuid(uid).pw_dir)
(home_dir / ".makepkg.conf").write_text(content, encoding="utf8") (home_dir / ".makepkg.conf").write_text(content, encoding="utf8")
@staticmethod
def configuration_create_sudo(paths: RepositoryPaths, repository_id: RepositoryId) -> None:
"""
create configuration to run build command with sudo without password
Args:
paths(RepositoryPaths): repository paths instance
repository_id(RepositoryId): repository unique identifier
"""
command = Setup.build_command(paths.root, repository_id)
sudoers_file = Setup.build_command(Setup.SUDOERS_DIR_PATH, repository_id)
sudoers_file.write_text(f"ahriman ALL=(ALL) NOPASSWD:SETENV: {command} *\n", encoding="utf8")
sudoers_file.chmod(0o400) # security!
@staticmethod
def executable_create(paths: RepositoryPaths, repository_id: RepositoryId) -> None:
"""
create executable for the service
Args:
paths(RepositoryPaths): repository paths instance
repository_id(RepositoryId): repository unique identifier
"""
command = Setup.build_command(paths.root, repository_id)
command.unlink(missing_ok=True)
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
arguments = [_set_service_setup_parser] arguments = [_set_service_setup_parser]
@@ -56,7 +56,7 @@ class PackageVersion(LazyLogging):
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
paths = configuration.repository_paths paths = configuration.repository_paths
task = Task(self.package, configuration, repository_id.architecture, paths) task = Task(self.package, configuration, repository_id, paths)
try: try:
# create fresh chroot environment, fetch sources and - automagically - update PKGBUILD # create fresh chroot environment, fetch sources and - automagically - update PKGBUILD
@@ -27,6 +27,7 @@ from ahriman.core.log import LazyLogging
from ahriman.core.utils import check_output, package_like from ahriman.core.utils import check_output, package_like
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@@ -36,32 +37,33 @@ class Task(LazyLogging):
Attributes: Attributes:
archbuild_flags(list[str]): command flags for archbuild command archbuild_flags(list[str]): command flags for archbuild command
architecture(str): repository architecture build_command(list[str]): build command
build_command(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
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
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance
repository_id(RepositoryId): repository unique identifier
uid(int): uid of the repository owner user uid(int): uid of the repository owner user
""" """
def __init__(self, package: Package, configuration: Configuration, architecture: str, def __init__(self, package: Package, configuration: Configuration, repository_id: RepositoryId,
paths: RepositoryPaths) -> None: paths: RepositoryPaths) -> None:
""" """
Args: Args:
package(Package): package definitions package(Package): package definitions
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
architecture(str): repository architecture repository_id(RepositoryId): repository unique identifier
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance
""" """
self.package = package self.package = package
self.paths = paths self.paths = paths
self.uid, _ = paths.root_owner self.uid, _ = paths.root_owner
self.architecture = architecture self.repository_id = repository_id
self.archbuild_flags = configuration.getlist("build", "archbuild_flags", fallback=[]) self.archbuild_flags = configuration.getlist("build", "archbuild_flags", fallback=[])
self.build_command = configuration.get("build", "build_command") self.build_command = configuration.getlist("build", "devtools_wrapper")
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)
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=[])
@@ -108,12 +110,16 @@ class Task(LazyLogging):
Returns: Returns:
list[Path]: paths of produced packages list[Path]: paths of produced packages
""" """
command = [self.build_command, "-r", str(self.paths.chroot)] command = self._legacy_build_command[:]
command.extend(self.archbuild_flags) if not command:
command.extend(["--", "-D", str(self.paths.archive)] + self.makechrootpkg_flags) command = self.build_command + ["-r", self.repository_id.name, "-a", self.repository_id.architecture, "--"]
command.extend(["--"] + self.makepkg_flags)
command.extend(["-r", str(self.paths.chroot)] + self.archbuild_flags) # archbuild flags
command.extend(["--", "-D", str(self.paths.archive)] + self.makechrootpkg_flags) # makechrootpkg flags
command.extend(["--"] + self.makepkg_flags) # makepkg flags
if dry_run: if dry_run:
command.extend(["--nobuild"]) command.extend(["--nobuild"])
self.logger.info("using %s for %s", command, self.package.base) self.logger.info("using %s for %s", command, self.package.base)
environment: dict[str, str] = { environment: dict[str, str] = {
@@ -156,7 +162,7 @@ class Task(LazyLogging):
return last_commit_sha return last_commit_sha
# load fresh package # load fresh package
loaded_package = Package.from_build(sources_dir, self.architecture, None) loaded_package = Package.from_build(sources_dir, self.repository_id.architecture, None)
if (pkgrel := loaded_package.next_pkgrel(local_version)) is not None: if (pkgrel := loaded_package.next_pkgrel(local_version)) is not None:
self.logger.info("package %s is the same as in repo, bumping pkgrel to %s", self.package.base, pkgrel) self.logger.info("package %s is the same as in repo, bumping pkgrel to %s", self.package.base, pkgrel)
patch = PkgbuildPatch("pkgrel", pkgrel) patch = PkgbuildPatch("pkgrel", pkgrel)
@@ -190,8 +190,20 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
}, },
}, },
"build_command": { "build_command": {
"type": "string", "type": "list",
"required": True, "coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
},
"devtools_wrapper": {
"type": "list",
"coerce": "list",
"schema": {
"type": "string",
"empty": False,
},
"empty": False, "empty": False,
}, },
"ignore_packages": { "ignore_packages": {
@@ -73,7 +73,7 @@ class Executor(PackageInfo, Cleaner):
""" """
self.reporter.set_building(package.base) self.reporter.set_building(package.base)
task = Task(package, self.configuration, self.repository_id.architecture, 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)
@@ -55,8 +55,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, 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") 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") 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")
@@ -67,8 +65,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
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) makepkg_configuration_mock.assert_called_once_with(args.packager, args.makeflags_jobs, repository_paths)
sudo_configuration_mock.assert_called_once_with(repository_paths, repository_id)
executable_mock.assert_called_once_with(repository_paths, repository_id)
init_mock.assert_called_once_with() init_mock.assert_called_once_with()
@@ -102,8 +98,6 @@ def test_run_with_server(args: argparse.Namespace, configuration: Configuration,
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.application.handlers.setup.Setup.configuration_create_makepkg")
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
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")
@@ -113,17 +107,6 @@ def test_run_with_server(args: argparse.Namespace, configuration: Configuration,
repository_id, args.from_configuration, args.mirror, args.multilib, "server") repository_id, args.from_configuration, args.mirror, args.multilib, "server")
def test_build_command(repository_id: RepositoryId) -> None:
"""
must generate correct build command name
"""
path = Path("local")
build_command = Setup.build_command(path, repository_id)
assert build_command.name == f"{repository_id.name}-{repository_id.architecture}-build"
assert build_command.parent == path
def test_configuration_create_ahriman(args: argparse.Namespace, configuration: Configuration, def test_configuration_create_ahriman(args: argparse.Namespace, configuration: Configuration,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
""" """
@@ -135,12 +118,9 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
write_mock = mocker.patch("ahriman.core.configuration.Configuration.write") write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
remove_mock = mocker.patch("pathlib.Path.unlink", autospec=True) remove_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
command = Setup.build_command(repository_paths.root, repository_id)
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(Configuration.section_name("build", repository_id.name, repository_id.architecture), "build_command",
str(command)),
MockCall("repository", "name", repository_id.name), MockCall("repository", "name", repository_id.name),
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}"),
@@ -251,34 +231,6 @@ def test_configuration_create_makepkg(args: argparse.Namespace, repository_paths
Path("home") / ".makepkg.conf", pytest.helpers.anyvar(str, True), encoding="utf8") Path("home") / ".makepkg.conf", pytest.helpers.anyvar(str, True), encoding="utf8")
def test_configuration_create_sudo(configuration: Configuration, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must create sudo configuration
"""
chmod_text_mock = mocker.patch("pathlib.Path.chmod")
write_text_mock = mocker.patch("pathlib.Path.write_text")
_, repository_id = configuration.check_loaded()
Setup.configuration_create_sudo(repository_paths, repository_id)
chmod_text_mock.assert_called_once_with(0o400)
write_text_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), encoding="utf8")
def test_executable_create(configuration: Configuration, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must create executable
"""
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
unlink_mock = mocker.patch("pathlib.Path.unlink")
_, repository_id = configuration.check_loaded()
Setup.executable_create(repository_paths, repository_id)
symlink_mock.assert_called_once_with(Setup.ARCHBUILD_COMMAND_PATH)
unlink_mock.assert_called_once_with(missing_ok=True)
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
@@ -63,7 +63,7 @@ def test_run_default(args: argparse.Namespace, configuration: Configuration) ->
default = Configuration.from_path(Configuration.SYSTEM_CONFIGURATION_PATH, repository_id) default = Configuration.from_path(Configuration.SYSTEM_CONFIGURATION_PATH, repository_id)
# copy autogenerated values # copy autogenerated values
for section, key in (("build", "build_command"), ("repository", "root")): for section, key in (("repository", "root"),):
value = configuration.get(section, key) value = configuration.get(section, key)
default.set_option(section, key, value) default.set_option(section, key, value)
@@ -53,8 +53,9 @@ def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
assert task_ahriman.build(local) == [task_ahriman.package.base] assert task_ahriman.build(local) == [task_ahriman.package.base]
check_output_mock.assert_called_once_with( check_output_mock.assert_called_once_with(
"extra-x86_64-build", "ahriman-archbuild",
"-r", str(task_ahriman.paths.chroot), "-r", task_ahriman.repository_id.name, "-a", task_ahriman.repository_id.architecture,
"--", "-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive), "--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck", "--", "--skippgpcheck",
exception=pytest.helpers.anyvar(int), exception=pytest.helpers.anyvar(int),
@@ -79,8 +80,9 @@ def test_build_environment(task_ahriman: Task, mocker: MockerFixture) -> None:
task_ahriman.build(local, **environment, empty=None) task_ahriman.build(local, **environment, empty=None)
check_output_mock.assert_called_once_with( check_output_mock.assert_called_once_with(
"extra-x86_64-build", "ahriman-archbuild",
"-r", str(task_ahriman.paths.chroot), "-r", task_ahriman.repository_id.name, "-a", task_ahriman.repository_id.architecture,
"--", "-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive), "--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck", "--", "--skippgpcheck",
exception=pytest.helpers.anyvar(int), exception=pytest.helpers.anyvar(int),
@@ -101,12 +103,37 @@ def test_build_dry_run(task_ahriman: Task, mocker: MockerFixture) -> None:
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] assert task_ahriman.build(local, dry_run=True) == [task_ahriman.package.base]
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",
"--nobuild",
exception=pytest.helpers.anyvar(int),
cwd=local,
logger=task_ahriman.logger,
user=task_ahriman.uid,
environment={},
)
def test_build_legacy_subcommand(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must build package by using legacy subcommand 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._legacy_build_command = ["extra-x86_64-build"]
task_ahriman.build(local)
check_output_mock.assert_called_once_with( check_output_mock.assert_called_once_with(
"extra-x86_64-build", "extra-x86_64-build",
"-r", str(task_ahriman.paths.chroot), "-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive), "--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck", "--", "--skippgpcheck",
"--nobuild",
exception=pytest.helpers.anyvar(int), exception=pytest.helpers.anyvar(int),
cwd=local, cwd=local,
logger=task_ahriman.logger, logger=task_ahriman.logger,
+2 -1
View File
@@ -76,4 +76,5 @@ def task_ahriman(package_ahriman: Package, configuration: Configuration, reposit
Returns: Returns:
Task: built task test instance Task: built task test instance
""" """
return Task(package_ahriman, configuration, "x86_64", repository_paths) _, repository_id = configuration.check_loaded()
return Task(package_ahriman, configuration, repository_id, repository_paths)
+4 -1
View File
@@ -12,7 +12,8 @@ depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-bcrypt' 'python-filelock' 'p
makedepends=('npm' 'python-build' 'python-hatchling' 'python-installer' 'python-wheel') makedepends=('npm' 'python-build' 'python-hatchling' 'python-installer' 'python-wheel')
source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgbase-$pkgver.tar.gz" source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgbase-$pkgver.tar.gz"
"$pkgbase.sysusers" "$pkgbase.sysusers"
"$pkgbase.tmpfiles") "$pkgbase.tmpfiles"
"sudoers.conf")
build() { build() {
cd "$pkgbase-$pkgver" cd "$pkgbase-$pkgver"
@@ -59,6 +60,8 @@ package_ahriman-core() {
install -Dm644 "$srcdir/$pkgbase.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgbase.conf" install -Dm644 "$srcdir/$pkgbase.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgbase.conf"
install -Dm644 "$srcdir/$pkgbase.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgbase.conf" install -Dm644 "$srcdir/$pkgbase.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgbase.conf"
install -Dm440 "$srcdir/sudoers.conf" "$pkgdir/etc/sudoers.d/$pkgname"
} }
package_ahriman-triggers() { package_ahriman-triggers() {
+1
View File
@@ -0,0 +1 @@
ahriman ALL= NOPASSWD:SETENV: /usr/bin/ahriman-archbuild *
+1 -1
View File
@@ -131,7 +131,7 @@ Authorized users are stored inside internal database, if any of external provide
Build related configuration. Group name can refer to architecture, e.g. ``build:x86_64`` can be used for x86_64 architecture specific settings. Build related configuration. Group name can refer to architecture, e.g. ``build:x86_64`` can be used for x86_64 architecture specific settings.
* ``archbuild_flags`` - additional flags passed to ``archbuild`` command, space separated list of strings, optional. * ``archbuild_flags`` - additional flags passed to ``archbuild`` command, space separated list of strings, optional.
* ``build_command`` - default build command, string, 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. * ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional.
+12 -36
View File
@@ -27,48 +27,24 @@ Initial setup
#. #.
Configure build tools (it is required for correct dependency management system): Configure build tools (it is required for correct dependency management system):
#. #.
Create build command (you can choose any name for command, basically it should be ``{name}-{arch}-build``): Create configuration file ``{name}.conf`` or ``{name}-{arch}.conf``, where ``name`` is the repostory name and ``arch`` is the repository architecture, e.g.:
.. code-block:: shell .. code-block:: shell
ln -s /usr/bin/archbuild /usr/local/bin/aur-x86_64-build cp /usr/share/devtools/pacman.conf.d/{extra,aur-x86_64}.conf
#. #.
Create configuration file (same as previous ``{name}.conf``): Change configuration file, add your own repository, add multilib repository etc:
.. code-block:: shell .. code-block:: shell
cp /usr/share/devtools/pacman.conf.d/{extra,aur}.conf echo '[multilib]' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
echo 'Include = /etc/pacman.d/mirrorlist' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
#. echo '[aur]' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
Change configuration file, add your own repository, add multilib repository etc: echo 'SigLevel = Optional TrustAll' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
echo 'Server = file:///var/lib/ahriman/repository/$repo/$arch' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
.. code-block:: shell
echo '[multilib]' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
echo 'Include = /etc/pacman.d/mirrorlist' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
echo '[aur]' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
echo 'SigLevel = Optional TrustAll' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
echo 'Server = file:///var/lib/ahriman/repository/$repo/$arch' | tee -a /usr/share/devtools/pacman.conf.d/aur-x86_64.conf
#.
Set ``build_command`` option to point to your command:
.. code-block:: shell
echo '[build]' | tee -a /etc/ahriman.ini.d/build.ini
echo 'build_command = aur-x86_64-build' | tee -a /etc/ahriman.ini.d/build.ini
#.
Configure ``/etc/sudoers.d/ahriman`` to allow running command without a password:
.. code-block:: shell
echo 'Cmnd_Alias CARCHBUILD_CMD = /usr/local/bin/aur-x86_64-build *' | tee -a /etc/sudoers.d/ahriman
echo 'ahriman ALL=(ALL) NOPASSWD:SETENV: CARCHBUILD_CMD' | tee -a /etc/sudoers.d/ahriman
chmod 400 /etc/sudoers.d/ahriman
This command supports several arguments, kindly refer to its help message. This command supports several arguments, kindly refer to its help message.
+1 -1
View File
@@ -23,7 +23,7 @@ allow_read_only = no
[build] [build]
archbuild_flags = archbuild_flags =
build_command = extra-x86_64-build devtools_wrapper = ahriman-archbuild
ignore_packages = ignore_packages =
makechrootpkg_flags = makechrootpkg_flags =
makepkg_flags = --skippgpcheck makepkg_flags = --skippgpcheck