add docker support (#52)

* add docker support

* make shellcheck happy
This commit is contained in:
2022-03-13 23:43:25 +03:00
committed by GitHub
parent 9964a96296
commit 046febc440
59 changed files with 429 additions and 103 deletions

View File

@ -72,6 +72,7 @@ def _parser() -> argparse.ArgumentParser:
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command", required=True)
_set_aur_search_parser(subparsers)
_set_help_commands_unsafe(subparsers)
_set_key_import_parser(subparsers)
_set_package_add_parser(subparsers)
_set_package_remove_parser(subparsers)
@ -118,6 +119,19 @@ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
def _set_help_commands_unsafe(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for listing unsafe commands
:param root: subparsers for the commands
:return: created argument parser
"""
parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
description="list unsafe commands as defined in default args", formatter_class=_formatter)
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, no_report=True, quiet=True,
unsafe=True, parser=_parser)
return parser
def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for key import subcommand
@ -396,6 +410,7 @@ def _set_repo_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="create initial service configuration, requires root",
epilog="Create _minimal_ configuration for the service according to provided options.",
formatter_class=_formatter)
parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
parser.add_argument("--build-command", help="build command prefix", default="ahriman")
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
type=Path, default=Path("/usr/share/devtools/pacman-extra.conf"))

View File

@ -32,14 +32,15 @@ class Properties:
:ivar repository: repository instance
"""
def __init__(self, architecture: str, configuration: Configuration, no_report: bool) -> None:
def __init__(self, architecture: str, configuration: Configuration, no_report: bool, unsafe: bool) -> None:
"""
default constructor
: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
"""
self.logger = logging.getLogger("root")
self.configuration = configuration
self.architecture = architecture
self.repository = Repository(architecture, configuration, no_report)
self.repository = Repository(architecture, configuration, no_report, unsafe)

View File

@ -35,6 +35,7 @@ from ahriman.application.handlers.sign import Sign
from ahriman.application.handlers.status import Status
from ahriman.application.handlers.status_update import StatusUpdate
from ahriman.application.handlers.sync import Sync
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
from ahriman.application.handlers.update import Update
from ahriman.application.handlers.user import User
from ahriman.application.handlers.web import Web

View File

@ -33,15 +33,16 @@ class Add(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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 = Application(architecture, configuration, no_report)
application = Application(architecture, configuration, no_report, unsafe)
application.add(args.package, args.source, args.without_dependencies)
if not args.now:
return

View File

@ -33,13 +33,14 @@ class Clean(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).clean(
Application(architecture, configuration, no_report, unsafe).clean(
args.build, args.cache, args.chroot, args.manual, args.packages, args.patches)

View File

@ -35,13 +35,14 @@ class Dump(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
dump = configuration.dump()
for section, values in sorted(dump.items()):

View File

@ -76,7 +76,7 @@ class Handler:
try:
configuration = Configuration.from_path(args.configuration, architecture, args.quiet)
with Lock(args, architecture, configuration):
cls.run(args, architecture, configuration, args.no_report)
cls.run(args, architecture, configuration, args.no_report, args.unsafe)
return True
except Exception:
# we are basically always want to print error to stderr instead of default logger
@ -107,12 +107,13 @@ class Handler:
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
raise NotImplementedError

View File

@ -35,12 +35,13 @@ class Init(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).repository.repo.init()
Application(architecture, configuration, no_report, unsafe).repository.repo.init()

View File

@ -35,12 +35,14 @@ class KeyImport(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).repository.sign.key_import(args.key_server, args.key)
Application(architecture, configuration, no_report, unsafe).repository.sign.key_import(
args.key_server, args.key)

View File

@ -41,15 +41,16 @@ class Patch(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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 = Application(architecture, configuration, no_report)
application = Application(architecture, configuration, no_report, unsafe)
if args.action == Action.List:
Patch.patch_set_list(application, args.package)

View File

@ -34,17 +34,18 @@ class Rebuild(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
depends_on = set(args.depends_on) if args.depends_on else None
application = Application(architecture, configuration, no_report)
application = Application(architecture, configuration, no_report, unsafe)
updates = application.repository.packages_depends_on(depends_on)
if args.dry_run:
for package in updates:

View File

@ -33,12 +33,13 @@ class Remove(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).remove(args.package)
Application(architecture, configuration, no_report, unsafe).remove(args.package)

View File

@ -34,15 +34,16 @@ class RemoveUnknown(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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 = Application(architecture, configuration, no_report)
application = Application(architecture, configuration, no_report, unsafe)
unknown_packages = application.unknown()
if args.dry_run:
for package in sorted(unknown_packages):

View File

@ -33,12 +33,13 @@ class Report(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).report(args.target, [])
Application(architecture, configuration, no_report, unsafe).report(args.target, [])

View File

@ -42,13 +42,14 @@ class Search(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
packages_list = AUR.multisearch(*args.search)
for package in Search.sort(packages_list, args.sort_by):

View File

@ -46,15 +46,16 @@ class Setup(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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 = Application(architecture, configuration, no_report)
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,
@ -87,6 +88,8 @@ class Setup(Handler):
section = Configuration.section_name("build", architecture)
configuration.set_option(section, "build_command", str(Setup.build_command(args.build_command, architecture)))
configuration.set_option("repository", "name", repository)
if args.build_as_user is not None:
configuration.set_option(section, "makechrootpkg_flags", f"-U {args.build_as_user}")
if args.sign_key is not None:
section = Configuration.section_name("sign", architecture)

View File

@ -33,12 +33,13 @@ class Sign(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).sign(args.package)
Application(architecture, configuration, no_report, unsafe).sign(args.package)

View File

@ -39,16 +39,17 @@ class Status(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
# we are using reporter here
client = Application(architecture, configuration, no_report=False).repository.reporter
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
if args.ahriman:
ahriman = client.get_self()
StatusPrinter(ahriman).print(args.info)

View File

@ -36,16 +36,17 @@ class StatusUpdate(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
# we are using reporter here
client = Application(architecture, configuration, no_report=False).repository.reporter
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
if args.action == Action.Update and args.package:
# update packages statuses

View File

@ -33,12 +33,13 @@ class Sync(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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).sync(args.target, [])
Application(architecture, configuration, no_report, unsafe).sync(args.target, [])

View File

@ -0,0 +1,58 @@
#
# 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 List, Type
from ahriman.application.formatters.string_printer import StringPrinter
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
class UnsafeCommands(Handler):
"""
unsafe command help parser
"""
@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
"""
unsafe_commands = UnsafeCommands.get_unsafe_commands(args.parser())
for command in unsafe_commands:
StringPrinter(command).print(verbose=True)
@staticmethod
def get_unsafe_commands(parser: argparse.ArgumentParser) -> List[str]:
"""
extract unsafe commands from argument parser
:param parser: generated argument parser
:return: list of commands with default unsafe flag
"""
# pylint: disable=protected-access
subparser = next(action for action in parser._actions if isinstance(action, argparse._SubParsersAction))
return [action_name for action_name, action in subparser.choices.items() if action.get_default("unsafe")]

View File

@ -33,15 +33,16 @@ class Update(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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 = Application(architecture, configuration, no_report)
application = Application(architecture, configuration, no_report, unsafe)
packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
Update.log_fn(application, args.dry_run))
if args.dry_run:

View File

@ -40,13 +40,14 @@ class User(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
salt = User.get_salt(configuration)
user = User.user_create(args)
@ -58,7 +59,7 @@ class User(Handler):
User.configuration_write(auth_configuration, args.secure)
if not args.no_reload:
client = Application(architecture, configuration, no_report=False).repository.reporter
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
client.reload_auth()
@staticmethod

View File

@ -36,13 +36,14 @@ class Web(Handler):
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
configuration: Configuration, no_report: bool) -> None:
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
"""
# we are using local import for optional dependencies
from ahriman.web.web import run_server, setup_service

View File

@ -103,9 +103,7 @@ class Lock:
"""
check if current user is actually owner of ahriman root
"""
if self.unsafe:
return
check_user(self.root)
check_user(self.root, self.unsafe)
def clear(self) -> None:
"""

View File

@ -45,12 +45,13 @@ class Properties:
:ivar sign: GPG wrapper instance
"""
def __init__(self, architecture: str, configuration: Configuration, no_report: bool) -> None:
def __init__(self, architecture: str, configuration: Configuration, no_report: bool, unsafe: bool) -> None:
"""
default constructor
: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
"""
self.logger = logging.getLogger("root")
self.architecture = architecture
@ -61,7 +62,7 @@ class Properties:
self.paths = RepositoryPaths(configuration.getpath("repository", "root"), architecture)
try:
check_user(self.paths.root)
check_user(self.paths.root, unsafe)
self.paths.tree_create()
except UnsafeRun:
self.logger.warning("root owner differs from the current user, skipping tree creation")

View File

@ -49,7 +49,7 @@ class Watcher:
self.logger = logging.getLogger("http")
self.architecture = architecture
self.repository = Repository(architecture, configuration, no_report=True)
self.repository = Repository(architecture, configuration, no_report=True, unsafe=False)
self.known: Dict[str, Tuple[Package, BuildStatus]] = {}
self.status = BuildStatus()

View File

@ -55,13 +55,16 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
raise exception or e
def check_user(root: Path) -> None:
def check_user(root: Path, unsafe: bool) -> None:
"""
check if current user is the owner of the root
:param root: root directory (i.e. ahriman home)
:param unsafe: if set no user check will be performed before path creation
"""
if not 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
if current_uid != root_uid: