Skip to content

Commit

Permalink
refactor(build): use custom exceptions in build (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
saranyailla authored Feb 23, 2022
1 parent 81fccee commit 9a19c68
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 79 deletions.
76 changes: 35 additions & 41 deletions gdk/commands/component/BuildCommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -54,8 +54,18 @@ def run(self):
logging.info("Running the following command\n{}".format(custom_build_command))
sp.run(custom_build_command)
else:
logging.info(f"Using '{build_system}' build system to build the component.")
self.default_build_component()
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)

def create_gg_build_directories(self):
"""
Expand Down Expand Up @@ -97,17 +107,14 @@ def default_build_component(self):
-------
None
"""
try:
# Build the project with specified build system
self.run_build_command()
# 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()
except Exception as e:
raise Exception("""{}\n{}""".format(error_messages.BUILD_FAILED, e))
# Update recipe file with component configuration from project config file.
self.create_build_recipe_file()

def get_build_cmd_from_platform(self, build_system):
"""
Expand Down Expand Up @@ -142,21 +149,14 @@ def run_build_command(self):
-------
None
"""
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}")
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)

def _build_system_zip(self):
"""
Expand Down Expand Up @@ -192,9 +192,8 @@ 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 Exception as e:
raise Exception("""Failed to zip the component in default build mode.\n{}""".format(e))
except RecursionError as e:
raise ZipBuildRecursionException(e)

def _ignore_files_during_zip(self, path, names):
"""
Expand Down Expand Up @@ -323,9 +322,7 @@ 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 Exception(
"Could not find artifact with URI '{}' on s3 or inside the build folders.".format(artifact["URI"])
)
raise ArtifactNotFoundException(artifact["URI"])

def is_artifact_in_build(self, artifact, build_folders):
"""
Expand Down Expand Up @@ -415,11 +412,8 @@ 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:
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))
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)
5 changes: 4 additions & 1 deletion gdk/commands/component/component.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from gdk.common.exceptions.BuildError import BuildException


def init(d_args):
from gdk.commands.component.InitCommand import InitCommand

Expand All @@ -13,7 +16,7 @@ def build(d_args):
try:
BuildCommand(d_args).run()
except Exception as e:
raise Exception(f"Could not build the project due to the following error.\n{e}")
raise BuildException("Could not build the project due to the following error.", e)


def publish(d_args):
Expand Down
28 changes: 28 additions & 0 deletions gdk/common/exceptions/BuildError.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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)
2 changes: 0 additions & 2 deletions gdk/common/exceptions/error_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@
"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."
13 changes: 7 additions & 6 deletions integration_tests/gdk/components/test_integ_BuildCommand.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
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.error_messages as error_messages
import gdk.common.exceptions.BuildError as BuildError
import gdk.common.parse_args_actions as parse_args_actions
import gdk.common.utils as utils
import pytest
Expand Down Expand Up @@ -303,7 +302,9 @@ 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=Exception("error in default_build_component")
BuildCommand,
"default_build_component",
side_effect=BuildError.BuildSystemException("error in default_build_component"),
)
mock_get_proj_config = mocker.patch(
"gdk.commands.component.project_utils.get_project_config_values",
Expand All @@ -328,7 +329,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=Error("err in run_build_command")
BuildCommand, "run_build_command", side_effect=BuildError.ZipBuildRecursionException("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")
Expand All @@ -341,7 +342,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 error_messages.BUILD_FAILED in e.value.args[0]
assert "Could not build the project due to the following error." 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
Expand Down Expand Up @@ -448,7 +449,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 "Failed to create build recipe file at" in e.value.args[0]
assert "writing failed" 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
Expand Down
Loading

0 comments on commit 9a19c68

Please sign in to comment.