diff --git a/gdk/commands/component/BuildCommand.py b/gdk/commands/component/BuildCommand.py index 39eb1dc4..fe36b3e3 100644 --- a/gdk/commands/component/BuildCommand.py +++ b/gdk/commands/component/BuildCommand.py @@ -7,10 +7,10 @@ import gdk.commands.component.project_utils as project_utils import gdk.common.consts as consts +import gdk.common.exceptions.error_messages as error_messages import gdk.common.utils as utils import yaml from gdk.commands.Command import Command -from gdk.common.exceptions.BuildError import ArtifactNotFoundException, BuildSystemException, ZipBuildRecursionException class BuildCommand(Command): @@ -54,18 +54,8 @@ def run(self): logging.info("Running the following command\n{}".format(custom_build_command)) sp.run(custom_build_command) else: - logging.info( - f"This component is identified as using '{build_system}' build system. If this is incorrect, please exit and" - f" specify custom build command in the '{consts.cli_project_config_file}'." - ) - try: - self.default_build_component() - except ZipBuildRecursionException as e: - raise e - except ArtifactNotFoundException as e: - raise e - except Exception as e: - raise BuildSystemException(build_system, e) + logging.info(f"Using '{build_system}' build system to build the component.") + self.default_build_component() def create_gg_build_directories(self): """ @@ -107,14 +97,17 @@ def default_build_component(self): ------- None """ - # Build the project with specified build system - self.run_build_command() + try: + # Build the project with specified build system + self.run_build_command() - # From the recipe, copy necessary artifacts (depends on build system) to the build folder . - self.find_artifacts_and_update_uri() + # From the recipe, copy necessary artifacts (depends on build system) to the build folder . + self.find_artifacts_and_update_uri() - # Update recipe file with component configuration from project config file. - self.create_build_recipe_file() + # Update recipe file with component configuration from project config file. + self.create_build_recipe_file() + except Exception as e: + raise Exception("""{}\n{}""".format(error_messages.BUILD_FAILED, e)) def get_build_cmd_from_platform(self, build_system): """ @@ -149,14 +142,21 @@ def run_build_command(self): ------- None """ - build_system = self.project_config["component_build_config"]["build_system"] - build_command = self.get_build_cmd_from_platform(build_system) - if build_system == "zip": - logging.info("Zipping source code files of the component.") - self._build_system_zip() - else: - logging.info("Running the build command '{}'".format(" ".join(build_command))) - sp.run(build_command) + try: + build_system = self.project_config["component_build_config"]["build_system"] + build_command = self.get_build_cmd_from_platform(build_system) + logging.warning( + f"This component is identified as using '{build_system}' build system. If this is incorrect, please exit and" + f" specify custom build command in the '{consts.cli_project_config_file}'." + ) + if build_system == "zip": + logging.info("Zipping source code files of the component.") + self._build_system_zip() + else: + logging.info("Running the build command '{}'".format(" ".join(build_command))) + sp.run(build_command) + except Exception as e: + raise Exception(f"Error building the component with the given build system.\n{e}") def _build_system_zip(self): """ @@ -192,8 +192,9 @@ def _build_system_zip(self): archive_file_name = Path(zip_build).joinpath(archive_file).resolve() shutil.make_archive(archive_file_name, "zip", root_dir=artifacts_zip_build) logging.debug("Archive complete.") - except RecursionError as e: - raise ZipBuildRecursionException(e) + + except Exception as e: + raise Exception("""Failed to zip the component in default build mode.\n{}""".format(e)) def _ignore_files_during_zip(self, path, names): """ @@ -322,7 +323,9 @@ def find_artifacts_and_update_uri(self): if not s3_client: s3_client = project_utils.create_s3_client(self.project_config["region"]) if not self.is_artifact_in_s3(s3_client, artifact["URI"]): - raise ArtifactNotFoundException(artifact["URI"]) + raise Exception( + "Could not find artifact with URI '{}' on s3 or inside the build folders.".format(artifact["URI"]) + ) def is_artifact_in_build(self, artifact, build_folders): """ @@ -412,8 +415,11 @@ def create_build_recipe_file(self): gg_build_recipe_file = Path(self.project_config["gg_build_recipes_dir"]).joinpath(component_recipe_file_name).resolve() with open(gg_build_recipe_file, "w") as recipe_file: - logging.info("Creating component recipe in '{}'.".format(self.project_config["gg_build_recipes_dir"])) - if component_recipe_file_name.endswith(".json"): - recipe_file.write(json.dumps(parsed_component_recipe, indent=4)) - else: - yaml.dump(parsed_component_recipe, recipe_file) + try: + logging.info("Creating component recipe in '{}'.".format(self.project_config["gg_build_recipes_dir"])) + if component_recipe_file_name.endswith(".json"): + recipe_file.write(json.dumps(parsed_component_recipe, indent=4)) + else: + yaml.dump(parsed_component_recipe, recipe_file) + except Exception as e: + raise Exception("""Failed to create build recipe file at '{}'.\n{}""".format(gg_build_recipe_file, e)) diff --git a/gdk/commands/component/component.py b/gdk/commands/component/component.py index 0bbcd701..361d87e4 100644 --- a/gdk/commands/component/component.py +++ b/gdk/commands/component/component.py @@ -1,6 +1,3 @@ -from gdk.common.exceptions.BuildError import BuildException - - def init(d_args): from gdk.commands.component.InitCommand import InitCommand @@ -16,7 +13,7 @@ def build(d_args): try: BuildCommand(d_args).run() except Exception as e: - raise BuildException("Could not build the project due to the following error.", e) + raise Exception(f"Could not build the project due to the following error.\n{e}") def publish(d_args): diff --git a/gdk/common/exceptions/BuildError.py b/gdk/common/exceptions/BuildError.py deleted file mode 100644 index 5fea9fd3..00000000 --- a/gdk/common/exceptions/BuildError.py +++ /dev/null @@ -1,28 +0,0 @@ -class BuildException(Exception): - def __init__(self, reason="", solution="", e=None): - err_details = "" - if e: - err_details = f"Error details: {e}\n" - self.message = f"{reason}\n{err_details}{solution}" - super().__init__(self.message) - - -class BuildSystemException(BuildException): - def __init__(self, build_system, e=None): - reason = f"Failed to build the component with the given build system '{build_system}'." - solution = "Please fix the component build errors and build the component again." - super().__init__(reason, solution, e) - - -class ZipBuildRecursionException(BuildException): - def __init__(self, e=None): - reason = "Failed to create an archive inside the zip-build folder." - solution = "Please remove symlinks in the gdk project directory if any and try again." - super().__init__(reason, solution, e) - - -class ArtifactNotFoundException(BuildException): - def __init__(self, uri, e=None): - reason = f"Could not find the artifact with URI '{uri}' in S3 or inside the build folders." - solution = "Please check the artifact URI in the recipe and build the component again." - super().__init__(reason, solution, e) diff --git a/gdk/common/exceptions/error_messages.py b/gdk/common/exceptions/error_messages.py index 7f40d3f9..832b2186 100644 --- a/gdk/common/exceptions/error_messages.py +++ b/gdk/common/exceptions/error_messages.py @@ -39,6 +39,8 @@ "Could not initialize the project as the directory '{}' already exists. Please initialize the project with a new" " directory.\nTry `gdk component init --help`" ) +# BUILD COMMAND +BUILD_FAILED = "Failed to build the component with the given project configuration." # PUBLISH COMMAND PUBLISH_FAILED = "Failed to publish new version of component with the given configuration." diff --git a/integration_tests/gdk/components/test_integ_BuildCommand.py b/integration_tests/gdk/components/test_integ_BuildCommand.py index 50b36fbf..dc7e2df2 100644 --- a/integration_tests/gdk/components/test_integ_BuildCommand.py +++ b/integration_tests/gdk/components/test_integ_BuildCommand.py @@ -1,10 +1,11 @@ import json from pathlib import Path +from shutil import Error from unittest.mock import mock_open, patch import gdk.CLIParser as CLIParser import gdk.common.consts as consts -import gdk.common.exceptions.BuildError as BuildError +import gdk.common.exceptions.error_messages as error_messages import gdk.common.parse_args_actions as parse_args_actions import gdk.common.utils as utils import pytest @@ -302,9 +303,7 @@ def test_build_run_default_gradle_yaml_artifact_not_found(mocker, supported_buil def test_build_run_default_exception(mocker, rglob_build_file): mock_create_gg_build_directories = mocker.patch.object(BuildCommand, "create_gg_build_directories") mock_default_build_component = mocker.patch.object( - BuildCommand, - "default_build_component", - side_effect=BuildError.BuildSystemException("error in default_build_component"), + BuildCommand, "default_build_component", side_effect=Exception("error in default_build_component") ) mock_get_proj_config = mocker.patch( "gdk.commands.component.project_utils.get_project_config_values", @@ -329,7 +328,7 @@ def test_default_build_component_error_run_build_command(mocker, rglob_build_fil mock_clean_dir = mocker.patch("gdk.common.utils.clean_dir", return_value=None) mock_create_dir = mocker.patch("pathlib.Path.mkdir", return_value=None) mock_run_build_command = mocker.patch.object( - BuildCommand, "run_build_command", side_effect=BuildError.ZipBuildRecursionException("err in run_build_command") + BuildCommand, "run_build_command", side_effect=Error("err in run_build_command") ) mock_find_artifacts_and_update_uri = mocker.patch.object(BuildCommand, "find_artifacts_and_update_uri") mock_create_build_recipe_file = mocker.patch.object(BuildCommand, "create_build_recipe_file") @@ -342,7 +341,7 @@ def test_default_build_component_error_run_build_command(mocker, rglob_build_fil ) with pytest.raises(Exception) as e: parse_args_actions.run_command(CLIParser.cli_parser.parse_args(["component", "build"])) - assert "Could not build the project due to the following error." in e.value.args[0] + assert error_messages.BUILD_FAILED in e.value.args[0] assert mock_run_build_command.assert_called_once assert not mock_find_artifacts_and_update_uri.called assert not mock_create_build_recipe_file.called @@ -449,7 +448,7 @@ def test_build_run_default_gradle_yaml_error_creating_recipe(mocker, supported_b parse_args_actions.run_command(CLIParser.cli_parser.parse_args(["component", "build"])) mock_file.assert_any_call(file_name, "w") mock_yaml_dump.call_count == 1 - assert "writing failed" in e.value.args[0] + assert "Failed to create build recipe file at" in e.value.args[0] assert mock_get_proj_config.assert_called_once mock_subprocess_run.assert_called_with(["gradle", "build"]) # called gradle build command assert mock_copy_dir.call_count == 0 # No copying directories diff --git a/tests/gdk/commands/component/test_BuildCommand.py b/tests/gdk/commands/component/test_BuildCommand.py index e380d197..74dc9f8e 100644 --- a/tests/gdk/commands/component/test_BuildCommand.py +++ b/tests/gdk/commands/component/test_BuildCommand.py @@ -6,7 +6,7 @@ import gdk.common.utils as utils import pytest from gdk.commands.component.BuildCommand import BuildCommand -from gdk.common.exceptions.BuildError import ArtifactNotFoundException, BuildSystemException, ZipBuildRecursionException +from gdk.common.exceptions import error_messages class BuildCommandTest(TestCase): @@ -34,65 +34,6 @@ def test_build_run_default(self): assert not mock_subprocess_run.called assert mock_get_supported_component_builds.called - def test_build_run_default_zip_exception(self): - mock_create_gg_build_directories = self.mocker.patch.object(BuildCommand, "create_gg_build_directories") - mock_default_build_component = self.mocker.patch.object( - BuildCommand, "default_build_component", side_effect=ZipBuildRecursionException("error archiving") - ) - - mock_get_supported_component_builds = self.mocker.patch( - "gdk.commands.component.project_utils.get_supported_component_builds", return_value={} - ) - mock_subprocess_run = self.mocker.patch("subprocess.run") - - with pytest.raises(Exception) as e: - BuildCommand({}).run() - assert "Failed to create an archive inside the zip-build folder." in e.value.args[0] - assert self.mock_get_proj_config.assert_called_once - assert mock_create_gg_build_directories.assert_called_once - assert mock_default_build_component.assert_called_once - assert not mock_subprocess_run.called - assert mock_get_supported_component_builds.called - - def test_build_run_default_artifact_not_found_exception(self): - mock_create_gg_build_directories = self.mocker.patch.object(BuildCommand, "create_gg_build_directories") - mock_default_build_component = self.mocker.patch.object( - BuildCommand, "default_build_component", side_effect=ArtifactNotFoundException("uri") - ) - - mock_get_supported_component_builds = self.mocker.patch( - "gdk.commands.component.project_utils.get_supported_component_builds", return_value={} - ) - mock_subprocess_run = self.mocker.patch("subprocess.run") - - with pytest.raises(Exception) as e: - BuildCommand({}).run() - assert "Could not find the artifact with URI" in e.value.args[0] - assert self.mock_get_proj_config.assert_called_once - assert mock_create_gg_build_directories.assert_called_once - assert mock_default_build_component.assert_called_once - assert not mock_subprocess_run.called - assert mock_get_supported_component_builds.called - - def test_build_run_default_build_system_exception(self): - mock_create_gg_build_directories = self.mocker.patch.object(BuildCommand, "create_gg_build_directories") - mock_default_build_component = self.mocker.patch.object( - BuildCommand, "default_build_component", side_effect=BuildSystemException("error in build") - ) - - mock_get_supported_component_builds = self.mocker.patch( - "gdk.commands.component.project_utils.get_supported_component_builds", return_value={} - ) - mock_subprocess_run = self.mocker.patch("subprocess.run") - with pytest.raises(Exception) as e: - BuildCommand({}).run() - assert "Failed to build the component with the given build system" in e.value.args[0] - assert self.mock_get_proj_config.assert_called_once - assert mock_create_gg_build_directories.assert_called_once - assert mock_default_build_component.assert_called_once - assert not mock_subprocess_run.called - assert mock_get_supported_component_builds.called - def test_build_run_custom(self): mock_create_gg_build_directories = self.mocker.patch.object(BuildCommand, "create_gg_build_directories") mock_default_build_component = self.mocker.patch.object(BuildCommand, "default_build_component") @@ -130,7 +71,7 @@ def test_default_build_component(self): def test_default_build_component_error_find_artifacts_and_update_uri(self): mock_run_build_command = self.mocker.patch.object(BuildCommand, "run_build_command") mock_find_artifacts_and_update_uri = self.mocker.patch.object( - BuildCommand, "find_artifacts_and_update_uri", side_effect=Error("error copying") + BuildCommand, "find_artifacts_and_update_uri", side_effect=Error("copying") ) mock_get_supported_component_builds = self.mocker.patch( @@ -142,7 +83,8 @@ def test_default_build_component_error_find_artifacts_and_update_uri(self): with pytest.raises(Exception) as e: build.default_build_component() - assert "error copying" in e.value.args[0] + assert "\ncopy" in e.value.args[0] + assert error_messages.BUILD_FAILED in e.value.args[0] assert mock_run_build_command.assert_called_once assert mock_find_artifacts_and_update_uri.assert_called_once assert not mock_create_build_recipe_file.called @@ -153,7 +95,7 @@ def test_default_build_component_error_create_build_recipe_file(self): mock_run_build_command = self.mocker.patch.object(BuildCommand, "run_build_command") mock_find_artifacts_and_update_uri = self.mocker.patch.object(BuildCommand, "find_artifacts_and_update_uri") mock_create_build_recipe_file = self.mocker.patch.object( - BuildCommand, "create_build_recipe_file", side_effect=Error("error in recipe") + BuildCommand, "create_build_recipe_file", side_effect=Error("recipe") ) mock_get_supported_component_builds = self.mocker.patch( @@ -163,7 +105,8 @@ def test_default_build_component_error_create_build_recipe_file(self): with pytest.raises(Exception) as e: build.default_build_component() - assert "error in recipe" in e.value.args[0] + assert "\nrecipe" in e.value.args[0] + assert error_messages.BUILD_FAILED in e.value.args[0] assert mock_run_build_command.assert_called_once assert mock_find_artifacts_and_update_uri.assert_called_once assert mock_create_build_recipe_file.assert_called_once @@ -191,7 +134,7 @@ def test_create_gg_build_directories(self): def test_run_build_command_with_error_not_zip(self): mock_build_system_zip = self.mocker.patch.object(BuildCommand, "_build_system_zip", return_value=None) - mock_subprocess_run = self.mocker.patch("subprocess.run", return_value=None, side_effect=Error("error in sp")) + mock_subprocess_run = self.mocker.patch("subprocess.run", return_value=None, side_effect=Error("some error")) mock_get_cmd_from_platform = self.mocker.patch.object( BuildCommand, "get_build_cmd_from_platform", return_value="some-command" ) @@ -206,7 +149,7 @@ def test_run_build_command_with_error_not_zip(self): build.run_build_command() assert not mock_build_system_zip.called assert mock_subprocess_run.called - assert "error in sp" in e.value.args[0] + assert "Error building the component with the given build system." in e.value.args[0] assert mock_get_supported_component_builds.called assert mock_get_cmd_from_platform.called @@ -224,9 +167,9 @@ def test_run_build_command_with_error_with_zip_build(self): ) build = BuildCommand({}) build.project_config["component_build_config"]["build_system"] = "zip" - with pytest.raises(Error) as e: + with pytest.raises(Exception) as e: build.run_build_command() - assert "some error" in e.value.args[0] + assert "Error building the component with the given build system." in e.value.args[0] assert mock_build_system_zip.called assert mock_get_cmd_from_platform.call_count == 1 assert mock_get_supported_component_builds.call_count == 1 @@ -381,7 +324,7 @@ def test_build_system_zip_error_archive(self): mock_copytree = self.mocker.patch("shutil.copytree") mock_subprocess_run = self.mocker.patch("subprocess.run", return_value=None) mock_ignore_files_during_zip = self.mocker.patch.object(BuildCommand, "_ignore_files_during_zip", return_value=[]) - mock_make_archive = self.mocker.patch("shutil.make_archive", side_effect=RecursionError("some error in archive")) + mock_make_archive = self.mocker.patch("shutil.make_archive", side_effect=Error("some error")) build_sys = { "zip": { @@ -393,12 +336,10 @@ def test_build_system_zip_error_archive(self): "gdk.commands.component.project_utils.get_supported_component_builds", return_value=build_sys ) build = BuildCommand({}) - with pytest.raises(ZipBuildRecursionException) as e: + with pytest.raises(Exception) as e: build._build_system_zip() - assert ( - "Failed to create an archive inside the zip-build folder.\nError details: some error in archive" in e.value.args[0] - ) + assert "Failed to zip the component in default build mode." in e.value.args[0] assert not mock_subprocess_run.called mock_build_info.assert_called_with() mock_clean_dir.assert_called_with(zip_build_path) @@ -420,7 +361,7 @@ def test_build_system_zip_error_copytree(self): BuildCommand, "_get_build_folder_by_build_system", return_value={zip_build_path} ) mock_clean_dir = self.mocker.patch("gdk.common.utils.clean_dir", return_value=None) - mock_copytree = self.mocker.patch("shutil.copytree", side_effect=Error("some error in copytree")) + mock_copytree = self.mocker.patch("shutil.copytree", side_effect=Error("some error")) mock_subprocess_run = self.mocker.patch("subprocess.run", return_value=None) mock_ignore_files_during_zip = self.mocker.patch.object(BuildCommand, "_ignore_files_during_zip", return_value=[]) @@ -438,7 +379,7 @@ def test_build_system_zip_error_copytree(self): with pytest.raises(Exception) as e: build._build_system_zip() - assert "some error in copytree" in e.value.args[0] + assert "Failed to zip the component in default build mode." in e.value.args[0] assert not mock_subprocess_run.called mock_build_info.assert_called_with() mock_clean_dir.assert_called_with(zip_build_path) @@ -455,7 +396,7 @@ def test_build_system_zip_error_get_build_folder_by_build_system(self): BuildCommand, "_get_build_folder_by_build_system", return_value=zip_build_path, - side_effect=Error("error in getting build system"), + side_effect=Error("some error"), ) mock_clean_dir = self.mocker.patch("gdk.common.utils.clean_dir", return_value=None) mock_copytree = self.mocker.patch("shutil.copytree") @@ -477,7 +418,7 @@ def test_build_system_zip_error_get_build_folder_by_build_system(self): with pytest.raises(Exception) as e: build._build_system_zip() - assert "error in getting build system" in e.value.args[0] + assert "Failed to zip the component in default build mode." in e.value.args[0] assert not mock_subprocess_run.called mock_build_info.assert_called_with() assert not mock_clean_dir.called @@ -491,9 +432,7 @@ def test_build_system_zip_error_clean_dir(self): mock_build_info = self.mocker.patch.object( BuildCommand, "_get_build_folder_by_build_system", return_value={zip_build_path} ) - mock_clean_dir = self.mocker.patch( - "gdk.common.utils.clean_dir", return_value=None, side_effect=Error("some error cleaning directory") - ) + mock_clean_dir = self.mocker.patch("gdk.common.utils.clean_dir", return_value=None, side_effect=Error("some error")) mock_copytree = self.mocker.patch("shutil.copytree") mock_subprocess_run = self.mocker.patch("subprocess.run", return_value=None) mock_make_archive = self.mocker.patch("shutil.make_archive") @@ -512,7 +451,7 @@ def test_build_system_zip_error_clean_dir(self): with pytest.raises(Exception) as e: build._build_system_zip() - assert "some error cleaning directory" in e.value.args[0] + assert "Failed to zip the component in default build mode." in e.value.args[0] assert not mock_subprocess_run.called mock_build_info.assert_called_with() assert mock_clean_dir.called @@ -604,15 +543,14 @@ def test_find_artifacts_and_update_uri_recipe_uri_not_matches_all(self): build = BuildCommand({}) mock_is_artifact_in_s3 = self.mocker.patch.object(BuildCommand, "is_artifact_in_s3", return_value=False) - with pytest.raises(ArtifactNotFoundException) as e: + with pytest.raises(Exception) as e: build.find_artifacts_and_update_uri() + assert ( - "Could not find the artifact with URI" - " 's3://DOC-EXAMPLE-BUCKET/artifacts/com.example.HelloWorld/1.0.0/hello_world.py' in S3 or inside the build" - " folders." + "Could not find artifact with URI 's3://DOC-EXAMPLE-BUCKET/artifacts/com.example.HelloWorld/1.0.0/hello_world.py'" + " on s3 or inside the build folders." in e.value.args[0] ) - assert not mock_shutil_copy.called assert mock_build_info.assert_called_once assert mock_glob.assert_called_once @@ -1011,7 +949,7 @@ def throw_error(*args, **kwargs): with patch("builtins.open", mock_open()) as mock_file: with pytest.raises(Exception) as e: build.create_build_recipe_file() - assert "I mock json error" in e.value.args[0] + assert "Failed to create build recipe file at" in e.value.args[0] mock_file.assert_called_once_with(file_name, "w") mock_json_dump.call_count == 1 assert not mock_yaml_dump.called @@ -1033,7 +971,7 @@ def throw_error(*args, **kwargs): with patch("builtins.open", mock_open()) as mock_file: with pytest.raises(Exception) as e: build.create_build_recipe_file() - assert "I mock yaml error" in e.value.args[0] + assert "Failed to create build recipe file at" in e.value.args[0] mock_file.assert_called_once_with(file_name, "w") mock_json_dump.call_count == 1 assert mock_yaml_dump.called diff --git a/uat/test_uat_build.py b/uat/test_uat_build.py index da1341f2..8fb317e3 100644 --- a/uat/test_uat_build.py +++ b/uat/test_uat_build.py @@ -66,11 +66,11 @@ def test_build_template_zip_fail_with_no_artifact(change_test_dir, gdk_cli): assert check_build_template.returncode == 1 assert Path(dir_path).joinpath("zip-build").resolve().exists() assert ( - "Could not find the artifact with URI 's3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/HelloWorld.zip' in S3 or" - " inside the build folders." + "Could not find artifact with URI 's3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/HelloWorld.zip' on s3 or inside" + " the build folders." in output ) - assert "Could not build the project due to the following error." in output + assert "Failed to build the component with the given project configuration." in output def test_build_template_maven(change_test_dir, gdk_cli):