From e182e110d421dc52970d638dec5c03d517a11711 Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Fri, 7 Aug 2026 14:35:20 +0300 Subject: [PATCH] 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 --- README.md | 2 +- ahriman-core/package/bin/ahriman-archbuild | 33 +++++++++++++ .../share/ahriman/settings/ahriman.ini | 2 +- ahriman-core/pyproject.toml | 1 + .../src/ahriman/application/handlers/setup.py | 49 ------------------- .../core/build_tools/package_version.py | 2 +- .../src/ahriman/core/build_tools/task.py | 28 ++++++----- .../src/ahriman/core/configuration/schema.py | 16 +++++- .../src/ahriman/core/repository/executor.py | 2 +- .../handlers/test_handler_setup.py | 48 ------------------ .../handlers/test_handler_validate.py | 2 +- .../ahriman/core/build_tools/test_task.py | 37 ++++++++++++-- ahriman-core/tests/ahriman/core/conftest.py | 3 +- archlinux/PKGBUILD | 5 +- archlinux/sudoers.conf | 1 + docs/configuration.rst | 2 +- docs/setup.rst | 48 +++++------------- tests/testresources/core/ahriman.ini | 2 +- 18 files changed, 123 insertions(+), 160 deletions(-) create mode 100755 ahriman-core/package/bin/ahriman-archbuild create mode 100644 archlinux/sudoers.conf diff --git a/README.md b/README.md index dcf45216..3be6db6d 100644 --- a/README.md +++ b/README.md @@ -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). -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) diff --git a/ahriman-core/package/bin/ahriman-archbuild b/ahriman-core/package/bin/ahriman-archbuild new file mode 100755 index 00000000..e1999e35 --- /dev/null +++ b/ahriman-core/package/bin/ahriman-archbuild @@ -0,0 +1,33 @@ +#!/bin/bash + +set -euo pipefail + +usage() { + local cmd="${0##*/}" + echo "Usage: $cmd [options] -- [archbuild args]" + echo " -r Repository name" + echo " -a 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}" diff --git a/ahriman-core/package/share/ahriman/settings/ahriman.ini b/ahriman-core/package/share/ahriman/settings/ahriman.ini index e6942879..b7679328 100644 --- a/ahriman-core/package/share/ahriman/settings/ahriman.ini +++ b/ahriman-core/package/share/ahriman/settings/ahriman.ini @@ -35,7 +35,7 @@ retry_backoff = 1.0 ; List of additional flags passed to archbuild command. ;archbuild_flags = ; Path to build command. -;build_command = +devtools_wrapper = ahriman-archbuild ; List of packages to be ignored during automatic updates. ;ignore_packages = ; Include debug packages. diff --git a/ahriman-core/pyproject.toml b/ahriman-core/pyproject.toml index f1884372..5c2d5060 100644 --- a/ahriman-core/pyproject.toml +++ b/ahriman-core/pyproject.toml @@ -57,6 +57,7 @@ packages = [ ] [tool.hatch.build.targets.wheel.shared-data] +"package/bin" = "bin" "package/lib" = "lib" "package/share" = "share" diff --git a/ahriman-core/src/ahriman/application/handlers/setup.py b/ahriman-core/src/ahriman/application/handlers/setup.py index 2d9aa3c6..b424685b 100644 --- a/ahriman-core/src/ahriman/application/handlers/setup.py +++ b/ahriman-core/src/ahriman/application/handlers/setup.py @@ -40,16 +40,12 @@ class Setup(Handler): setup handler Attributes: - ARCHBUILD_COMMAND_PATH(Path): (class attribute) default devtools command 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 - ARCHBUILD_COMMAND_PATH: ClassVar[Path] = Path("/") / "usr" / "bin" / "archbuild" MIRRORLIST_PATH: ClassVar[Path] = Path("/") / "etc" / "pacman.d" / "mirrorlist" - SUDOERS_DIR_PATH: ClassVar[Path] = Path("/") / "etc" / "sudoers.d" @classmethod 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 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 Setup.configuration_create_devtools( repository_id, args.from_configuration, args.mirror, args.multilib, repository_server) - Setup.configuration_create_sudo(application.repository.paths, repository_id) # finish initialization with application.repository.paths.preserve_owner(): @@ -124,20 +118,6 @@ class Setup(Handler): parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True) 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 def configuration_create_ahriman(args: argparse.Namespace, repository_id: RepositoryId, root: Configuration) -> None: @@ -152,8 +132,6 @@ class Setup(Handler): configuration = Configuration() 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 if args.build_as_user is not None: 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 / ".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] diff --git a/ahriman-core/src/ahriman/core/build_tools/package_version.py b/ahriman-core/src/ahriman/core/build_tools/package_version.py index 44e178e1..7d487ee3 100644 --- a/ahriman-core/src/ahriman/core/build_tools/package_version.py +++ b/ahriman-core/src/ahriman/core/build_tools/package_version.py @@ -56,7 +56,7 @@ class PackageVersion(LazyLogging): _, repository_id = configuration.check_loaded() paths = configuration.repository_paths - task = Task(self.package, configuration, repository_id.architecture, paths) + task = Task(self.package, configuration, repository_id, paths) try: # create fresh chroot environment, fetch sources and - automagically - update PKGBUILD diff --git a/ahriman-core/src/ahriman/core/build_tools/task.py b/ahriman-core/src/ahriman/core/build_tools/task.py index 970c59af..e4c7ef4f 100644 --- a/ahriman-core/src/ahriman/core/build_tools/task.py +++ b/ahriman-core/src/ahriman/core/build_tools/task.py @@ -27,6 +27,7 @@ from ahriman.core.log import LazyLogging from ahriman.core.utils import check_output, package_like from ahriman.models.package import Package from ahriman.models.pkgbuild_patch import PkgbuildPatch +from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_paths import RepositoryPaths @@ -36,32 +37,33 @@ class Task(LazyLogging): Attributes: archbuild_flags(list[str]): command flags for archbuild command - architecture(str): repository architecture - build_command(str): build command + build_command(list[str]): build command include_debug_packages(bool): whether to include debug packages or not makechrootpkg_flags(list[str]): command flags for makechrootpkg command makepkg_flags(list[str]): command flags for makepkg command package(Package): package definitions paths(RepositoryPaths): repository paths instance + repository_id(RepositoryId): repository unique identifier 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: """ Args: package(Package): package definitions configuration(Configuration): configuration instance - architecture(str): repository architecture + repository_id(RepositoryId): repository unique identifier paths(RepositoryPaths): repository paths instance """ self.package = package self.paths = paths self.uid, _ = paths.root_owner - self.architecture = architecture + self.repository_id = repository_id 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.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[]) self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[]) @@ -108,12 +110,16 @@ class Task(LazyLogging): Returns: list[Path]: paths of produced packages """ - command = [self.build_command, "-r", str(self.paths.chroot)] - command.extend(self.archbuild_flags) - command.extend(["--", "-D", str(self.paths.archive)] + self.makechrootpkg_flags) - command.extend(["--"] + self.makepkg_flags) + command = self._legacy_build_command[:] + if not command: + command = self.build_command + ["-r", self.repository_id.name, "-a", self.repository_id.architecture, "--"] + + 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: command.extend(["--nobuild"]) + self.logger.info("using %s for %s", command, self.package.base) environment: dict[str, str] = { @@ -156,7 +162,7 @@ class Task(LazyLogging): return last_commit_sha # 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: self.logger.info("package %s is the same as in repo, bumping pkgrel to %s", self.package.base, pkgrel) patch = PkgbuildPatch("pkgrel", pkgrel) diff --git a/ahriman-core/src/ahriman/core/configuration/schema.py b/ahriman-core/src/ahriman/core/configuration/schema.py index a56c69f0..328ec73a 100644 --- a/ahriman-core/src/ahriman/core/configuration/schema.py +++ b/ahriman-core/src/ahriman/core/configuration/schema.py @@ -190,8 +190,20 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = { }, }, "build_command": { - "type": "string", - "required": True, + "type": "list", + "coerce": "list", + "schema": { + "type": "string", + "empty": False, + }, + }, + "devtools_wrapper": { + "type": "list", + "coerce": "list", + "schema": { + "type": "string", + "empty": False, + }, "empty": False, }, "ignore_packages": { diff --git a/ahriman-core/src/ahriman/core/repository/executor.py b/ahriman-core/src/ahriman/core/repository/executor.py index 5e1dedcb..5a9d81c7 100644 --- a/ahriman-core/src/ahriman/core/repository/executor.py +++ b/ahriman-core/src/ahriman/core/repository/executor.py @@ -73,7 +73,7 @@ class Executor(PackageInfo, Cleaner): """ 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) commit_sha = task.init(path, patches, local_version) diff --git a/ahriman-core/tests/ahriman/application/handlers/test_handler_setup.py b/ahriman-core/tests/ahriman/application/handlers/test_handler_setup.py index 17f14661..fe8a984d 100644 --- a/ahriman-core/tests/ahriman/application/handlers/test_handler_setup.py +++ b/ahriman-core/tests/ahriman/application/handlers/test_handler_setup.py @@ -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") 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") - 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") 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( 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) - 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() @@ -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.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_sudo") - mocker.patch("ahriman.application.handlers.setup.Setup.executable_create") mocker.patch("ahriman.core.alpm.repo.Repo.init") 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") -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, 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") remove_mock = mocker.patch("pathlib.Path.unlink", autospec=True) _, repository_id = configuration.check_loaded() - command = Setup.build_command(repository_paths.root, repository_id) Setup.configuration_create_ahriman(args, repository_id, configuration) 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(Configuration.section_name("build", repository_id.name, repository_id.architecture), "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") -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: """ must not allow multi architecture run diff --git a/ahriman-core/tests/ahriman/application/handlers/test_handler_validate.py b/ahriman-core/tests/ahriman/application/handlers/test_handler_validate.py index 5d4281e2..076e6dba 100644 --- a/ahriman-core/tests/ahriman/application/handlers/test_handler_validate.py +++ b/ahriman-core/tests/ahriman/application/handlers/test_handler_validate.py @@ -63,7 +63,7 @@ def test_run_default(args: argparse.Namespace, configuration: Configuration) -> default = Configuration.from_path(Configuration.SYSTEM_CONFIGURATION_PATH, repository_id) # copy autogenerated values - for section, key in (("build", "build_command"), ("repository", "root")): + for section, key in (("repository", "root"),): value = configuration.get(section, key) default.set_option(section, key, value) diff --git a/ahriman-core/tests/ahriman/core/build_tools/test_task.py b/ahriman-core/tests/ahriman/core/build_tools/test_task.py index 7c00466c..21222a12 100644 --- a/ahriman-core/tests/ahriman/core/build_tools/test_task.py +++ b/ahriman-core/tests/ahriman/core/build_tools/test_task.py @@ -53,8 +53,9 @@ def test_build(task_ahriman: Task, mocker: MockerFixture) -> None: assert task_ahriman.build(local) == [task_ahriman.package.base] check_output_mock.assert_called_once_with( - "extra-x86_64-build", - "-r", str(task_ahriman.paths.chroot), + "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", 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) check_output_mock.assert_called_once_with( - "extra-x86_64-build", - "-r", str(task_ahriman.paths.chroot), + "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", 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") 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( "extra-x86_64-build", "-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, diff --git a/ahriman-core/tests/ahriman/core/conftest.py b/ahriman-core/tests/ahriman/core/conftest.py index e9d6f916..e7ced622 100644 --- a/ahriman-core/tests/ahriman/core/conftest.py +++ b/ahriman-core/tests/ahriman/core/conftest.py @@ -76,4 +76,5 @@ def task_ahriman(package_ahriman: Package, configuration: Configuration, reposit Returns: 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) diff --git a/archlinux/PKGBUILD b/archlinux/PKGBUILD index 8a0fefc3..7de6b486 100644 --- a/archlinux/PKGBUILD +++ b/archlinux/PKGBUILD @@ -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') source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgbase-$pkgver.tar.gz" "$pkgbase.sysusers" - "$pkgbase.tmpfiles") + "$pkgbase.tmpfiles" + "sudoers.conf") build() { 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.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgbase.conf" + + install -Dm440 "$srcdir/sudoers.conf" "$pkgdir/etc/sudoers.d/$pkgname" } package_ahriman-triggers() { diff --git a/archlinux/sudoers.conf b/archlinux/sudoers.conf new file mode 100644 index 00000000..3ffc193c --- /dev/null +++ b/archlinux/sudoers.conf @@ -0,0 +1 @@ +ahriman ALL= NOPASSWD:SETENV: /usr/bin/ahriman-archbuild * diff --git a/docs/configuration.rst b/docs/configuration.rst index 0c523731..14f81fec 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -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. * ``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. * ``include_debug_packages`` - distribute debug packages, boolean, optional, default ``yes``. * ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional. diff --git a/docs/setup.rst b/docs/setup.rst index eafbd26b..d54fad81 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -27,48 +27,24 @@ Initial setup #. 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 - #. - Change configuration file, add your own repository, add multilib repository etc: - - .. 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 + 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 This command supports several arguments, kindly refer to its help message. diff --git a/tests/testresources/core/ahriman.ini b/tests/testresources/core/ahriman.ini index 36715f8f..b65987d6 100644 --- a/tests/testresources/core/ahriman.ini +++ b/tests/testresources/core/ahriman.ini @@ -23,7 +23,7 @@ allow_read_only = no [build] archbuild_flags = -build_command = extra-x86_64-build +devtools_wrapper = ahriman-archbuild ignore_packages = makechrootpkg_flags = makepkg_flags = --skippgpcheck