implicit type conversion from command line

This commit is contained in:
Evgenii Alekseev 2021-04-04 23:53:30 +03:00
parent ffe6aec190
commit 0bd3ba626a
7 changed files with 79 additions and 22 deletions

View File

@ -20,16 +20,17 @@
import argparse import argparse
import sys import sys
from pathlib import Path
import ahriman.application.handlers as handlers import ahriman.application.handlers as handlers
import ahriman.version as version import ahriman.version as version
from ahriman.models.build_status import BuildStatusEnum from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.sign_settings import SignSettings
# pylint thinks it is bad idea, but get the fuck off # pylint thinks it is bad idea, but get the fuck off
# pylint: disable=protected-access # pylint: disable=protected-access
from ahriman.models.sign_settings import SignSettings
SubParserAction = argparse._SubParsersAction SubParserAction = argparse._SubParsersAction
@ -42,9 +43,9 @@ def _parser() -> argparse.ArgumentParser:
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-a", "--architecture", help="target architectures (can be used multiple times)", parser.add_argument("-a", "--architecture", help="target architectures (can be used multiple times)",
action="append", required=True) action="append", required=True)
parser.add_argument("-c", "--configuration", help="configuration path", default="/etc/ahriman.ini") parser.add_argument("-c", "--configuration", help="configuration path", type=Path, default=Path("/etc/ahriman.ini"))
parser.add_argument("--force", help="force run, remove file lock", action="store_true") parser.add_argument("--force", help="force run, remove file lock", action="store_true")
parser.add_argument("--lock", help="lock file", default="/tmp/ahriman.lock") parser.add_argument("-l", "--lock", help="lock file", type=Path, default=Path("/tmp/ahriman.lock"))
parser.add_argument("--no-log", help="redirect all log messages to stderr", action="store_true") parser.add_argument("--no-log", help="redirect all log messages to stderr", action="store_true")
parser.add_argument("--no-report", help="force disable reporting to web service", action="store_true") parser.add_argument("--no-report", help="force disable reporting to web service", action="store_true")
parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user", action="store_true") parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user", action="store_true")
@ -180,13 +181,13 @@ def _set_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--build-command", help="build command prefix", default="ahriman") parser.add_argument("--build-command", help="build command prefix", default="ahriman")
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration", parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
default="/usr/share/devtools/pacman-extra.conf") type=Path, default=Path("/usr/share/devtools/pacman-extra.conf"))
parser.add_argument("--no-multilib", help="do not add multilib repository", action="store_true") parser.add_argument("--no-multilib", help="do not add multilib repository", action="store_true")
parser.add_argument("--packager", help="packager name and email", required=True) parser.add_argument("--packager", help="packager name and email", required=True)
parser.add_argument("--repository", help="repository name", required=True) parser.add_argument("--repository", help="repository name", required=True)
parser.add_argument("--sign-key", help="sign key id") parser.add_argument("--sign-key", help="sign key id")
parser.add_argument("--sign-target", help="sign options", parser.add_argument("--sign-target", help="sign options", type=SignSettings.from_option,
choices=[sign.name.lower() for sign in SignSettings], nargs="*") choices=SignSettings, nargs="*")
parser.add_argument("--web-port", help="port of the web service", type=int) parser.add_argument("--web-port", help="port of the web service", type=int)
parser.set_defaults(handler=handlers.Setup, lock=None, no_report=True, unsafe=True) parser.set_defaults(handler=handlers.Setup, lock=None, no_report=True, unsafe=True)
return parser return parser
@ -231,8 +232,8 @@ def _set_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
"package", "package",
help="set status for specified packages. If no packages supplied, service status will be updated", help="set status for specified packages. If no packages supplied, service status will be updated",
nargs="*") nargs="*")
parser.add_argument("--status", help="new status", choices=[value.value for value in BuildStatusEnum], parser.add_argument("--status", help="new status", choices=BuildStatusEnum,
default="success") type=BuildStatusEnum, default=BuildStatusEnum.Success)
parser.add_argument("--remove", help="remove package status page", action="store_true") parser.add_argument("--remove", help="remove package status page", action="store_true")
parser.set_defaults(handler=handlers.StatusUpdate, lock=None, no_report=True, unsafe=True) parser.set_defaults(handler=handlers.StatusUpdate, lock=None, no_report=True, unsafe=True)
return parser return parser

View File

@ -54,7 +54,7 @@ class Setup(Handler):
application = Application(architecture, configuration) application = Application(architecture, configuration)
Setup.create_makepkg_configuration(args.packager, application.repository.paths) Setup.create_makepkg_configuration(args.packager, application.repository.paths)
Setup.create_executable(args.build_command, architecture) Setup.create_executable(args.build_command, architecture)
Setup.create_devtools_configuration(args.build_command, architecture, Path(args.from_configuration), Setup.create_devtools_configuration(args.build_command, architecture, args.from_configuration,
args.no_multilib, args.repository, application.repository.paths) args.no_multilib, args.repository, application.repository.paths)
Setup.create_ahriman_configuration(args, architecture, args.repository, configuration.include) Setup.create_ahriman_configuration(args, architecture, args.repository, configuration.include)
Setup.create_sudo_configuration(args.build_command, architecture) Setup.create_sudo_configuration(args.build_command, architecture)
@ -91,7 +91,7 @@ class Setup(Handler):
if args.sign_key is not None: if args.sign_key is not None:
section = Configuration.section_name("sign", architecture) section = Configuration.section_name("sign", architecture)
configuration.add_section(section) configuration.add_section(section)
configuration.set(section, "target", " ".join(args.sign_target)) configuration.set(section, "target", " ".join([target.name.lower() for target in args.sign_target]))
configuration.set(section, "key", args.sign_key) configuration.set(section, "key", args.sign_key)
if args.web_port is not None: if args.web_port is not None:

View File

@ -24,7 +24,6 @@ from typing import Callable, Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.build_status import BuildStatusEnum
class StatusUpdate(Handler): class StatusUpdate(Handler):
@ -41,12 +40,11 @@ class StatusUpdate(Handler):
:param configuration: configuration instance :param configuration: configuration instance
""" """
client = Application(architecture, configuration).repository.reporter client = Application(architecture, configuration).repository.reporter
status = BuildStatusEnum(args.status) callback: Callable[[str], None] = lambda p: client.remove(p) if args.remove else client.update(p, args.status)
callback: Callable[[str], None] = lambda p: client.remove(p) if args.remove else client.update(p, status)
if args.package: if args.package:
# update packages statuses # update packages statuses
for package in args.package: for package in args.package:
callback(package) callback(package)
else: else:
# update service status # update service status
client.update_self(status) client.update_self(args.status)

View File

@ -1,6 +1,7 @@
import argparse import argparse
import pytest import pytest
from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -11,7 +12,7 @@ def test_call(args: argparse.Namespace, mocker: MockerFixture) -> None:
""" """
must call inside lock must call inside lock
""" """
args.configuration = "" args.configuration = Path("")
args.no_log = False args.no_log = False
mocker.patch("ahriman.application.handlers.Handler.run") mocker.patch("ahriman.application.handlers.Handler.run")
mocker.patch("ahriman.core.configuration.Configuration.from_path") mocker.patch("ahriman.core.configuration.Configuration.from_path")

View File

@ -7,16 +7,17 @@ from unittest import mock
from ahriman.application.handlers import Setup from ahriman.application.handlers import Setup
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.sign_settings import SignSettings
def _default_args(args: argparse.Namespace) -> argparse.Namespace: def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.build_command = "ahriman" args.build_command = "ahriman"
args.from_configuration = "/usr/share/devtools/pacman-extra.conf" args.from_configuration = Path("/usr/share/devtools/pacman-extra.conf")
args.no_multilib = False args.no_multilib = False
args.packager = "John Doe <john@doe.com>" args.packager = "John Doe <john@doe.com>"
args.repository = "aur-clone" args.repository = "aur-clone"
args.sign_key = "key" args.sign_key = "key"
args.sign_target = ["packages"] args.sign_target = [SignSettings.Packages]
args.web_port = 8080 args.web_port = 8080
return args return args
@ -71,7 +72,8 @@ def test_create_ahriman_configuration(args: argparse.Namespace, configuration: C
set_mock.assert_has_calls([ set_mock.assert_has_calls([
mock.call(Configuration.section_name("build", "x86_64"), "build_command", str(command)), mock.call(Configuration.section_name("build", "x86_64"), "build_command", str(command)),
mock.call("repository", "name", args.repository), mock.call("repository", "name", args.repository),
mock.call(Configuration.section_name("sign", "x86_64"), "target", " ".join(args.sign_target)), mock.call(Configuration.section_name("sign", "x86_64"), "target",
" ".join([target.name.lower() for target in args.sign_target])),
mock.call(Configuration.section_name("sign", "x86_64"), "key", args.sign_key), mock.call(Configuration.section_name("sign", "x86_64"), "key", args.sign_key),
mock.call(Configuration.section_name("web", "x86_64"), "port", str(args.web_port)), mock.call(Configuration.section_name("web", "x86_64"), "port", str(args.web_port)),
]) ])
@ -89,7 +91,7 @@ def test_create_devtools_configuration(args: argparse.Namespace, repository_path
add_section_mock = mocker.patch("configparser.RawConfigParser.add_section") add_section_mock = mocker.patch("configparser.RawConfigParser.add_section")
write_mock = mocker.patch("configparser.RawConfigParser.write") write_mock = mocker.patch("configparser.RawConfigParser.write")
Setup.create_devtools_configuration(args.build_command, "x86_64", Path(args.from_configuration), Setup.create_devtools_configuration(args.build_command, "x86_64", args.from_configuration,
args.no_multilib, args.repository, repository_paths) args.no_multilib, args.repository, repository_paths)
add_section_mock.assert_has_calls([ add_section_mock.assert_has_calls([
mock.call("multilib"), mock.call("multilib"),
@ -109,7 +111,7 @@ def test_create_devtools_configuration_no_multilib(args: argparse.Namespace, rep
add_section_mock = mocker.patch("configparser.RawConfigParser.add_section") add_section_mock = mocker.patch("configparser.RawConfigParser.add_section")
write_mock = mocker.patch("configparser.RawConfigParser.write") write_mock = mocker.patch("configparser.RawConfigParser.write")
Setup.create_devtools_configuration(args.build_command, "x86_64", Path(args.from_configuration), Setup.create_devtools_configuration(args.build_command, "x86_64", args.from_configuration,
True, args.repository, repository_paths) True, args.repository, repository_paths)
add_section_mock.assert_called_once() add_section_mock.assert_called_once()
write_mock.assert_called_once() write_mock.assert_called_once()

View File

@ -9,7 +9,7 @@ from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace: def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.status = BuildStatusEnum.Success.value args.status = BuildStatusEnum.Success
args.package = None args.package = None
args.remove = False args.remove = False
return args return args

View File

@ -1,8 +1,11 @@
import argparse import argparse
from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.sign_settings import SignSettings
def test_parser(parser: argparse.ArgumentParser) -> None: def test_parser(parser: argparse.ArgumentParser) -> None:
@ -12,6 +15,26 @@ def test_parser(parser: argparse.ArgumentParser) -> None:
parser.parse_args(["-a", "x86_64", "config"]) parser.parse_args(["-a", "x86_64", "config"])
def test_parser_option_configuration(parser: argparse.ArgumentParser) -> None:
"""
must convert configuration option to Path instance
"""
args = parser.parse_args(["-a", "x86_64", "config"])
assert isinstance(args.configuration, Path)
args = parser.parse_args(["-a", "x86_64", "-c", "ahriman.ini", "config"])
assert isinstance(args.configuration, Path)
def test_parser_option_lock(parser: argparse.ArgumentParser) -> None:
"""
must convert lock option to Path instance
"""
args = parser.parse_args(["-a", "x86_64", "update"])
assert isinstance(args.lock, Path)
args = parser.parse_args(["-a", "x86_64", "-l", "ahriman.lock", "update"])
assert isinstance(args.lock, Path)
def test_multiple_architectures(parser: argparse.ArgumentParser) -> None: def test_multiple_architectures(parser: argparse.ArgumentParser) -> None:
""" """
must accept multiple architectures must accept multiple architectures
@ -59,6 +82,28 @@ def test_subparsers_setup(parser: argparse.ArgumentParser) -> None:
assert args.unsafe assert args.unsafe
def test_subparsers_setup_option_from_configuration(parser: argparse.ArgumentParser) -> None:
"""
setup command must convert from-configuration option to path instance
"""
args = parser.parse_args(["-a", "x86_64", "setup", "--packager", "John Doe <john@doe.com>",
"--repository", "aur-clone"])
assert isinstance(args.from_configuration, Path)
args = parser.parse_args(["-a", "x86_64", "setup", "--packager", "John Doe <john@doe.com>",
"--repository", "aur-clone", "--from-configuration", "path"])
assert isinstance(args.from_configuration, Path)
def test_subparsers_setup_option_sign_target(parser: argparse.ArgumentParser) -> None:
"""
setup command must convert sign-target option to signsettings instance
"""
args = parser.parse_args(["-a", "x86_64", "setup", "--packager", "John Doe <john@doe.com>",
"--repository", "aur-clone", "--sign-target", "packages"])
assert args.sign_target
assert all(isinstance(target, SignSettings) for target in args.sign_target)
def test_subparsers_status(parser: argparse.ArgumentParser) -> None: def test_subparsers_status(parser: argparse.ArgumentParser) -> None:
""" """
status command must imply lock, no_report and unsafe status command must imply lock, no_report and unsafe
@ -79,6 +124,16 @@ def test_subparsers_status_update(parser: argparse.ArgumentParser) -> None:
assert args.unsafe assert args.unsafe
def test_subparsers_status_update_option_status(parser: argparse.ArgumentParser) -> None:
"""
status-update command must convert status option to buildstatusenum instance
"""
args = parser.parse_args(["-a", "x86_64", "status-update"])
assert isinstance(args.status, BuildStatusEnum)
args = parser.parse_args(["-a", "x86_64", "status-update", "--status", "failed"])
assert isinstance(args.status, BuildStatusEnum)
def test_subparsers_web(parser: argparse.ArgumentParser) -> None: def test_subparsers_web(parser: argparse.ArgumentParser) -> None:
""" """
web command must imply lock and no_report web command must imply lock and no_report