From 794256f1bc6cc20c87cff579c9d403a343391db7 Mon Sep 17 00:00:00 2001 From: Bulat Gaifullin Date: Fri, 24 Jun 2016 18:15:39 +0300 Subject: [PATCH] Added CLI for building packages Usage of command: packetary build -t "the name of driver" \ -i "the path of file with input data" \ -o "the path to output directory" \ [-C "the config for driver"] Change-Id: I71df1b8a36342a9ea238d5b6ebb686e5bce71007 --- packetary/cli/commands/base.py | 50 ++++++++++++- packetary/cli/commands/build.py | 67 +++++++++++++++++ .../tests/test_cli_packaging_commands.py | 71 +++++++++++++++++++ ..._commands.py => test_cli_repo_commands.py} | 11 +-- setup.cfg | 1 + 5 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 packetary/cli/commands/build.py create mode 100644 packetary/tests/test_cli_packaging_commands.py rename packetary/tests/{test_cli_commands.py => test_cli_repo_commands.py} (95%) diff --git a/packetary/cli/commands/base.py b/packetary/cli/commands/base.py index 916b9fe..83c2f38 100644 --- a/packetary/cli/commands/base.py +++ b/packetary/cli/commands/base.py @@ -28,7 +28,7 @@ from packetary import api @six.add_metaclass(abc.ABCMeta) -class BaseRepoCommand(command.Command): +class BaseCommand(command.Command): """Super class for packetary commands.""" @property @@ -36,6 +36,10 @@ class BaseRepoCommand(command.Command): """Shortcut for self.app.stdout.""" return self.app.stdout + +class BaseRepoCommand(BaseCommand): + """Class for repo commands.""" + def get_parser(self, prog_name): """Specifies common options.""" parser = super(BaseRepoCommand, self).get_parser(prog_name) @@ -200,3 +204,47 @@ class BaseProduceOutputCommand(BaseRepoCommand): @abc.abstractmethod def take_repo_action(self, driver, parsed_args): """See Command.take_repo_action.""" + + +class BasePackagingCommand(BaseCommand): + + def get_parser(self, prog_name): + """Specifies common options.""" + + parser = super(BasePackagingCommand, self).get_parser(prog_name) + parser.add_argument( + '-t', '--type', + required=True, + metavar='TYPE', + help='The type of package.' + ) + parser.add_argument( + '-C', '--driver-config', + required=False, + metavar='PATH', + help='The path to configuration file for used driver.' + ) + + return parser + + def take_action(self, parsed_args): + """See the Command.take_action. + + :param parsed_args: the command-line arguments + :return: the result of take_repo_action + :rtype: object + """ + return self.take_package_action( + api.PackagingApi.create( + self.app_args, parsed_args.type, parsed_args.driver_config + ), + parsed_args + ) + + def take_package_action(self, packaging_api, parsed_args): + """Takes action with package(s). + + :param packaging_api: the RepositoryApi instance + :param parsed_args: the command-line arguments + :return: the action result + """ diff --git a/packetary/cli/commands/build.py b/packetary/cli/commands/build.py new file mode 100644 index 0000000..fb3cbb3 --- /dev/null +++ b/packetary/cli/commands/build.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- + +# Copyright 2016 Mirantis, Inc. +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from packetary.cli.commands.base import BasePackagingCommand +from packetary.cli.commands.utils import read_from_file + + +class BuildPackageCommand(BasePackagingCommand): + """Builds the new package.""" + + def get_parser(self, prog_name): + parser = super(BuildPackageCommand, self).get_parser(prog_name) + parser.add_argument( + '-i', + '--input-data', + type=read_from_file, + required=True, + metavar='PATH', + help='The list of sources to build packages,' + 'the each source should contain path to source files and ' + 'path to spec file.' + ) + + parser.add_argument( + '-o', + '--output-dir', + required=False, + metavar='OUTPUT_DIR', + default='.', + help='The output directory, where will be saved packages, ' + 'which have been built.' + ) + return parser + + def take_package_action(self, packaging_api, parsed_args): + packages = packaging_api.build_packages( + parsed_args.input_data, parsed_args.output_dir + ) + self.stdout.write("Packages built:\n") + for package in packages: + self.stdout.write(package) + self.stdout.write("\n") + + +def debug(argv=None): + """Helper to debug the Build command.""" + from packetary.cli.app import debug + debug("build", BuildPackageCommand, argv) + + +if __name__ == "__main__": + debug() diff --git a/packetary/tests/test_cli_packaging_commands.py b/packetary/tests/test_cli_packaging_commands.py new file mode 100644 index 0000000..2ea60db --- /dev/null +++ b/packetary/tests/test_cli_packaging_commands.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +# Copyright 2016 Mirantis, Inc. +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import mock + +from packetary.api import PackagingApi +from packetary.cli.commands import build + +from packetary.tests import base + + +@mock.patch("packetary.cli.commands.base.BaseCommand.stdout") +@mock.patch("packetary.cli.commands.base.api.PackagingApi") +class TestCliPackagingCommands(base.TestCase): + common_argv = [ + "--ignore-errors-num=3", + "--threads-num=8", + "--retries-num=10", + "--retry-interval=1", + "--http-proxy=http://proxy", + "--https-proxy=https://proxy" + ] + + build_argv = [ + "-C", "driver.conf", + "-i", "packages.yaml", + "-o", "/tmp", + "-t", "test", + ] + + def start_cmd(self, cmd, argv): + cmd.debug(argv + self.common_argv) + + def get_api_instance_mock(self, api_mock): + api_instance = mock.MagicMock(spec=PackagingApi) + api_mock.create.return_value = api_instance + return api_instance + + @mock.patch("packetary.cli.commands.build.read_from_file") + def test_build_cmd(self, read_file_mock, api_mock, stdout_mock): + read_file_mock.side_effect = [[{"source": "/sources"}]] + api_instance = self.get_api_instance_mock(api_mock) + api_instance.build_packages.return_value = ['package1'] + self.start_cmd(build, self.build_argv) + api_mock.create.assert_called_once_with( + mock.ANY, "test", "driver.conf" + ) + read_file_mock.assert_called_once_with("packages.yaml") + api_instance.build_packages.assert_called_once_with( + [{"source": "/sources"}], "/tmp" + ) + stdout_mock.write.assert_has_calls([ + mock.call("Packages built:\n"), + mock.call("package1"), + mock.call("\n") + ]) diff --git a/packetary/tests/test_cli_commands.py b/packetary/tests/test_cli_repo_commands.py similarity index 95% rename from packetary/tests/test_cli_commands.py rename to packetary/tests/test_cli_repo_commands.py index b36e128..4afa3b1 100644 --- a/packetary/tests/test_cli_commands.py +++ b/packetary/tests/test_cli_repo_commands.py @@ -16,15 +16,8 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import subprocess - import mock -# The cmd2 does not work with python3.5 -# because it tries to get access to the property mswindows, -# that was removed in 3.5 -subprocess.mswindows = False - from packetary.api import RepositoryApi from packetary.api.statistics import CopyStatistics from packetary.cli.commands import clone @@ -38,10 +31,10 @@ from packetary.tests.stubs.generator import gen_relation from packetary.tests.stubs.generator import gen_repository -@mock.patch("packetary.cli.commands.base.BaseRepoCommand.stdout") +@mock.patch("packetary.cli.commands.base.BaseCommand.stdout") @mock.patch("packetary.cli.commands.base.read_from_file") @mock.patch("packetary.cli.commands.base.api.RepositoryApi") -class TestCliCommands(base.TestCase): +class TestCliRepoCommands(base.TestCase): common_argv = [ "--ignore-errors-num=3", "--threads-num=8", diff --git a/setup.cfg b/setup.cfg index 4ecdcc9..494fa82 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,6 +35,7 @@ packetary.repository_drivers = rpm=packetary.drivers.rpm_driver:RpmRepositoryDriver packetary = + build=packetary.cli.commands.build.BuildPackageCommand clone=packetary.cli.commands.clone:CloneCommand create=packetary.cli.commands.create:CreateCommand packages=packetary.cli.commands.packages:ListOfPackages