more options in setup command

This commit is contained in:
Evgenii Alekseev 2021-04-04 15:42:06 +03:00
parent 56c600e5ac
commit ffe6aec190
12 changed files with 75 additions and 47 deletions

View File

@ -41,4 +41,5 @@ command = rsync --archive --verbose --compress --partial --delete
command = aws s3 sync --quiet --delete command = aws s3 sync --quiet --delete
[web] [web]
host = 0.0.0.0
templates = /usr/share/ahriman templates = /usr/share/ahriman

View File

@ -28,6 +28,8 @@ from ahriman.models.build_status import BuildStatusEnum
# 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
@ -181,7 +183,11 @@ def _set_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
default="/usr/share/devtools/pacman-extra.conf") default="/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", default="aur-clone") parser.add_argument("--repository", help="repository name", required=True)
parser.add_argument("--sign-key", help="sign key id")
parser.add_argument("--sign-target", help="sign options",
choices=[sign.name.lower() for sign in SignSettings], nargs="*")
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

View File

@ -56,7 +56,7 @@ class Setup(Handler):
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, Path(args.from_configuration),
args.no_multilib, args.repository, application.repository.paths) args.no_multilib, args.repository, application.repository.paths)
Setup.create_ahriman_configuration(args.build_command, 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)
@staticmethod @staticmethod
@ -70,23 +70,36 @@ class Setup(Handler):
return Setup.BIN_DIR_PATH / f"{prefix}-{architecture}-build" return Setup.BIN_DIR_PATH / f"{prefix}-{architecture}-build"
@staticmethod @staticmethod
def create_ahriman_configuration(prefix: str, architecture: str, repository: str, include_path: Path) -> None: def create_ahriman_configuration(args: argparse.Namespace, architecture: str, repository: str,
include_path: Path) -> None:
""" """
create service specific configuration create service specific configuration
:param prefix: command prefix in {prefix}-{architecture}-build :param args: command line args
:param architecture: repository architecture :param architecture: repository architecture
:param repository: repository name :param repository: repository name
:param include_path: path to directory with configuration includes :param include_path: path to directory with configuration includes
""" """
configuration = configparser.ConfigParser() configuration = configparser.ConfigParser()
configuration.add_section("build") section = Configuration.section_name("build", architecture)
configuration.set("build", "build_command", str(Setup.build_command(prefix, architecture))) configuration.add_section(section)
configuration.set(section, "build_command", str(Setup.build_command(args.build_command, architecture)))
configuration.add_section("repository") configuration.add_section("repository")
configuration.set("repository", "name", repository) configuration.set("repository", "name", repository)
target = include_path / "build-overrides.ini" if args.sign_key is not None:
section = Configuration.section_name("sign", architecture)
configuration.add_section(section)
configuration.set(section, "target", " ".join(args.sign_target))
configuration.set(section, "key", args.sign_key)
if args.web_port is not None:
section = Configuration.section_name("web", architecture)
configuration.add_section(section)
configuration.set(section, "port", str(args.web_port))
target = include_path / "setup-overrides.ini"
with target.open("w") as ahriman_configuration: with target.open("w") as ahriman_configuration:
configuration.write(ahriman_configuration) configuration.write(ahriman_configuration)

View File

@ -112,8 +112,8 @@ class HTML(Report):
html = template.render( html = template.render(
homepage=self.homepage, homepage=self.homepage,
link_path=self.link_path, link_path=self.link_path,
has_package_signed=SignSettings.SignPackages in self.sign_targets, has_package_signed=SignSettings.Packages in self.sign_targets,
has_repo_signed=SignSettings.SignRepository in self.sign_targets, has_repo_signed=SignSettings.Repository in self.sign_targets,
packages=sorted(content, key=comparator), packages=sorted(content, key=comparator),
pgp_key=self.default_pgp_key, pgp_key=self.default_pgp_key,
repository=self.name) repository=self.name)

View File

@ -56,7 +56,7 @@ class GPG:
""" """
:return: command line arguments for repo-add command to sign database :return: command line arguments for repo-add command to sign database
""" """
if SignSettings.SignRepository not in self.targets: if SignSettings.Repository not in self.targets:
return [] return []
if self.default_key is None: if self.default_key is None:
self.logger.error("no default key set, skip repository sign") self.logger.error("no default key set, skip repository sign")
@ -107,7 +107,7 @@ class GPG:
:param base: package base required to check for key overrides :param base: package base required to check for key overrides
:return: list of generated files including original file :return: list of generated files including original file
""" """
if SignSettings.SignPackages not in self.targets: if SignSettings.Packages not in self.targets:
return [path] return [path]
key = self.configuration.get("sign", f"key_{base}", fallback=self.default_key) key = self.configuration.get("sign", f"key_{base}", fallback=self.default_key)
if key is None: if key is None:
@ -122,7 +122,7 @@ class GPG:
:param path: path to repository database :param path: path to repository database
:return: list of generated files including original file :return: list of generated files including original file
""" """
if SignSettings.SignRepository not in self.targets: if SignSettings.Repository not in self.targets:
return [path] return [path]
if self.default_key is None: if self.default_key is None:
self.logger.error("no default key set, skip repository sign") self.logger.error("no default key set, skip repository sign")

View File

@ -28,12 +28,12 @@ from ahriman.core.exceptions import InvalidOption
class SignSettings(Enum): class SignSettings(Enum):
""" """
sign targets enumeration sign targets enumeration
:cvar SignPackages: sign each package :cvar Packages: sign each package
:cvar SignRepository: sign repository database file :cvar Repository: sign repository database file
""" """
SignPackages = auto() Packages = auto()
SignRepository = auto() Repository = auto()
@classmethod @classmethod
def from_option(cls: Type[SignSettings], value: str) -> SignSettings: def from_option(cls: Type[SignSettings], value: str) -> SignSettings:
@ -43,7 +43,7 @@ class SignSettings(Enum):
:return: parsed value :return: parsed value
""" """
if value.lower() in ("package", "packages", "sign-package"): if value.lower() in ("package", "packages", "sign-package"):
return cls.SignPackages return cls.Packages
if value.lower() in ("repository", "sign-repository"): if value.lower() in ("repository", "sign-repository"):
return cls.SignRepository return cls.Repository
raise InvalidOption(value) raise InvalidOption(value)

View File

@ -15,6 +15,9 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
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_target = ["packages"]
args.web_port = 8080
return args return args
@ -58,14 +61,19 @@ def test_create_ahriman_configuration(args: argparse.Namespace, configuration: C
write_mock = mocker.patch("configparser.RawConfigParser.write") write_mock = mocker.patch("configparser.RawConfigParser.write")
command = Setup.build_command(args.build_command, "x86_64") command = Setup.build_command(args.build_command, "x86_64")
Setup.create_ahriman_configuration(args.build_command, "x86_64", args.repository, configuration.include) Setup.create_ahriman_configuration(args, "x86_64", args.repository, configuration.include)
add_section_mock.assert_has_calls([ add_section_mock.assert_has_calls([
mock.call("build"), mock.call(Configuration.section_name("build", "x86_64")),
mock.call("repository"), mock.call("repository"),
mock.call(Configuration.section_name("sign", "x86_64")),
mock.call(Configuration.section_name("web", "x86_64")),
]) ])
set_mock.assert_has_calls([ set_mock.assert_has_calls([
mock.call("build", "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"), "key", args.sign_key),
mock.call(Configuration.section_name("web", "x86_64"), "port", str(args.web_port)),
]) ])
write_mock.assert_called_once() write_mock.assert_called_once()

View File

@ -52,7 +52,8 @@ def test_subparsers_setup(parser: argparse.ArgumentParser) -> None:
""" """
setup command must imply lock, no_report and unsafe setup command must imply lock, no_report and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "setup", "--packager", "John Doe <john@doe.com>"]) args = parser.parse_args(["-a", "x86_64", "setup", "--packager", "John Doe <john@doe.com>",
"--repository", "aur-clone"])
assert args.lock is None assert args.lock is None
assert args.no_report assert args.no_report
assert args.unsafe assert args.unsafe

View File

@ -9,7 +9,7 @@ def test_repository_sign_args_1(gpg_with_key: GPG) -> None:
""" """
must generate correct sign args must generate correct sign args
""" """
gpg_with_key.targets = {SignSettings.SignRepository} gpg_with_key.targets = {SignSettings.Repository}
assert gpg_with_key.repository_sign_args assert gpg_with_key.repository_sign_args
@ -17,7 +17,7 @@ def test_repository_sign_args_2(gpg_with_key: GPG) -> None:
""" """
must generate correct sign args must generate correct sign args
""" """
gpg_with_key.targets = {SignSettings.SignPackages, SignSettings.SignRepository} gpg_with_key.targets = {SignSettings.Packages, SignSettings.Repository}
assert gpg_with_key.repository_sign_args assert gpg_with_key.repository_sign_args
@ -33,7 +33,7 @@ def test_repository_sign_args_skip_2(gpg_with_key: GPG) -> None:
""" """
must return empty args if it is not set must return empty args if it is not set
""" """
gpg_with_key.targets = {SignSettings.SignPackages} gpg_with_key.targets = {SignSettings.Packages}
assert not gpg_with_key.repository_sign_args assert not gpg_with_key.repository_sign_args
@ -41,7 +41,7 @@ def test_repository_sign_args_skip_3(gpg: GPG) -> None:
""" """
must return empty args if it is not set must return empty args if it is not set
""" """
gpg.targets = {SignSettings.SignRepository} gpg.targets = {SignSettings.Repository}
assert not gpg.repository_sign_args assert not gpg.repository_sign_args
@ -49,7 +49,7 @@ def test_repository_sign_args_skip_4(gpg: GPG) -> None:
""" """
must return empty args if it is not set must return empty args if it is not set
""" """
gpg.targets = {SignSettings.SignPackages, SignSettings.SignRepository} gpg.targets = {SignSettings.Packages, SignSettings.Repository}
assert not gpg.repository_sign_args assert not gpg.repository_sign_args
@ -78,7 +78,7 @@ def test_sign_package_1(gpg_with_key: GPG, mocker: MockerFixture) -> None:
result = [Path("a"), Path("a.sig")] result = [Path("a"), Path("a.sig")]
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result) process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result)
gpg_with_key.targets = {SignSettings.SignPackages} gpg_with_key.targets = {SignSettings.Packages}
assert gpg_with_key.sign_package(Path("a"), "a") == result assert gpg_with_key.sign_package(Path("a"), "a") == result
process_mock.assert_called_once() process_mock.assert_called_once()
@ -90,7 +90,7 @@ def test_sign_package_2(gpg_with_key: GPG, mocker: MockerFixture) -> None:
result = [Path("a"), Path("a.sig")] result = [Path("a"), Path("a.sig")]
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result) process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result)
gpg_with_key.targets = {SignSettings.SignPackages, SignSettings.SignRepository} gpg_with_key.targets = {SignSettings.Packages, SignSettings.Repository}
assert gpg_with_key.sign_package(Path("a"), "a") == result assert gpg_with_key.sign_package(Path("a"), "a") == result
process_mock.assert_called_once() process_mock.assert_called_once()
@ -110,7 +110,7 @@ def test_sign_package_skip_2(gpg_with_key: GPG, mocker: MockerFixture) -> None:
must not sign package if it is not set must not sign package if it is not set
""" """
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process") process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process")
gpg_with_key.targets = {SignSettings.SignRepository} gpg_with_key.targets = {SignSettings.Repository}
gpg_with_key.sign_package(Path("a"), "a") gpg_with_key.sign_package(Path("a"), "a")
process_mock.assert_not_called() process_mock.assert_not_called()
@ -120,7 +120,7 @@ def test_sign_package_skip_3(gpg: GPG, mocker: MockerFixture) -> None:
must not sign package if it is not set must not sign package if it is not set
""" """
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process") process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process")
gpg.targets = {SignSettings.SignPackages} gpg.targets = {SignSettings.Packages}
gpg.sign_package(Path("a"), "a") gpg.sign_package(Path("a"), "a")
process_mock.assert_not_called() process_mock.assert_not_called()
@ -130,7 +130,7 @@ def test_sign_package_skip_4(gpg: GPG, mocker: MockerFixture) -> None:
must not sign package if it is not set must not sign package if it is not set
""" """
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process") process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process")
gpg.targets = {SignSettings.SignPackages, SignSettings.SignRepository} gpg.targets = {SignSettings.Packages, SignSettings.Repository}
gpg.sign_package(Path("a"), "a") gpg.sign_package(Path("a"), "a")
process_mock.assert_not_called() process_mock.assert_not_called()
@ -142,7 +142,7 @@ def test_sign_repository_1(gpg_with_key: GPG, mocker: MockerFixture) -> None:
result = [Path("a"), Path("a.sig")] result = [Path("a"), Path("a.sig")]
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result) process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result)
gpg_with_key.targets = {SignSettings.SignRepository} gpg_with_key.targets = {SignSettings.Repository}
assert gpg_with_key.sign_repository(Path("a")) == result assert gpg_with_key.sign_repository(Path("a")) == result
process_mock.assert_called_once() process_mock.assert_called_once()
@ -154,7 +154,7 @@ def test_sign_repository_2(gpg_with_key: GPG, mocker: MockerFixture) -> None:
result = [Path("a"), Path("a.sig")] result = [Path("a"), Path("a.sig")]
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result) process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process", return_value=result)
gpg_with_key.targets = {SignSettings.SignPackages, SignSettings.SignRepository} gpg_with_key.targets = {SignSettings.Packages, SignSettings.Repository}
assert gpg_with_key.sign_repository(Path("a")) == result assert gpg_with_key.sign_repository(Path("a")) == result
process_mock.assert_called_once() process_mock.assert_called_once()
@ -174,7 +174,7 @@ def test_sign_repository_skip_2(gpg_with_key: GPG, mocker: MockerFixture) -> Non
must not sign repository if it is not set must not sign repository if it is not set
""" """
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process") process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process")
gpg_with_key.targets = {SignSettings.SignPackages} gpg_with_key.targets = {SignSettings.Packages}
gpg_with_key.sign_repository(Path("a")) gpg_with_key.sign_repository(Path("a"))
process_mock.assert_not_called() process_mock.assert_not_called()
@ -184,7 +184,7 @@ def test_sign_repository_skip_3(gpg: GPG, mocker: MockerFixture) -> None:
must not sign repository if it is not set must not sign repository if it is not set
""" """
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process") process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process")
gpg.targets = {SignSettings.SignRepository} gpg.targets = {SignSettings.Repository}
gpg.sign_repository(Path("a")) gpg.sign_repository(Path("a"))
process_mock.assert_not_called() process_mock.assert_not_called()
@ -194,6 +194,6 @@ def test_sign_repository_skip_4(gpg: GPG, mocker: MockerFixture) -> None:
must not sign repository if it is not set must not sign repository if it is not set
""" """
process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process") process_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process")
gpg.targets = {SignSettings.SignPackages, SignSettings.SignRepository} gpg.targets = {SignSettings.Packages, SignSettings.Repository}
gpg.sign_repository(Path("a")) gpg.sign_repository(Path("a"))
process_mock.assert_not_called() process_mock.assert_not_called()

View File

@ -16,11 +16,11 @@ def test_from_option_valid() -> None:
""" """
must return value from valid options must return value from valid options
""" """
assert SignSettings.from_option("package") == SignSettings.SignPackages assert SignSettings.from_option("package") == SignSettings.Packages
assert SignSettings.from_option("PACKAGE") == SignSettings.SignPackages assert SignSettings.from_option("PACKAGE") == SignSettings.Packages
assert SignSettings.from_option("packages") == SignSettings.SignPackages assert SignSettings.from_option("packages") == SignSettings.Packages
assert SignSettings.from_option("sign-package") == SignSettings.SignPackages assert SignSettings.from_option("sign-package") == SignSettings.Packages
assert SignSettings.from_option("repository") == SignSettings.SignRepository assert SignSettings.from_option("repository") == SignSettings.Repository
assert SignSettings.from_option("REPOSITORY") == SignSettings.SignRepository assert SignSettings.from_option("REPOSITORY") == SignSettings.Repository
assert SignSettings.from_option("sign-repository") == SignSettings.SignRepository assert SignSettings.from_option("sign-repository") == SignSettings.Repository

View File

@ -34,12 +34,10 @@ def test_run(application: web.Application, mocker: MockerFixture) -> None:
""" """
must run application must run application
""" """
host = "localhost"
port = 8080 port = 8080
application["configuration"].set("web", "host", host)
application["configuration"].set("web", "port", str(port)) application["configuration"].set("web", "port", str(port))
run_application_mock = mocker.patch("aiohttp.web.run_app") run_application_mock = mocker.patch("aiohttp.web.run_app")
run_server(application) run_server(application)
run_application_mock.assert_called_with(application, host=host, port=port, run_application_mock.assert_called_with(application, host="0.0.0.0", port=port,
handle_signals=False, access_log=pytest.helpers.anyvar(int)) handle_signals=False, access_log=pytest.helpers.anyvar(int))

View File

@ -43,4 +43,5 @@ bucket =
command = aws s3 sync --quiet --delete command = aws s3 sync --quiet --delete
[web] [web]
host = 0.0.0.0
templates = ../web/templates templates = ../web/templates