diff --git a/pull_request_codecommit/__init__.py b/pull_request_codecommit/__init__.py index 940ca96..24ceac1 100644 --- a/pull_request_codecommit/__init__.py +++ b/pull_request_codecommit/__init__.py @@ -16,13 +16,16 @@ def main( """ pull-request-codecommit """ - repo = __load_repository(repository_path=repository_path, target_branch=branch) - __display_repository_information(repo) - pr = __create_pull_request(repo) - - if auto_merge: - status = pr.merge() - click.echo(f"Auto merging resulted in: {status}") + try: + repo = __load_repository(repository_path=repository_path, target_branch=branch) + __display_repository_information(repo) + pr = __create_pull_request(repo) + + if auto_merge: + status = pr.merge() + click.echo(f"Auto merging resulted in: {status}") + except Exception as exception: + raise click.ClickException(str(exception)) def __load_repository( diff --git a/pull_request_codecommit/aws/client.py b/pull_request_codecommit/aws/client.py index 0ee1fbf..77e4c99 100644 --- a/pull_request_codecommit/aws/client.py +++ b/pull_request_codecommit/aws/client.py @@ -32,7 +32,16 @@ def base_command(self) -> List[str]: def __execute(self, parameters: List[str]) -> str: command = self.base_command command.extend(parameters) - response = subprocess.run(command, stdout=subprocess.PIPE) + response = subprocess.run( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + if response.returncode != 0: + message = response.stderr.decode("utf-8").strip("\n") + last_line = message.splitlines()[-1] + raise Exception( + f"Failed to execute: `{command}`\n\n{last_line}\n\nYou can execute the command manually to troubleshoot!" + ) return response.stdout.decode("utf-8").strip("\n") diff --git a/pull_request_codecommit/git/client.py b/pull_request_codecommit/git/client.py index d938355..d0e8e99 100644 --- a/pull_request_codecommit/git/client.py +++ b/pull_request_codecommit/git/client.py @@ -18,10 +18,23 @@ def __init__(self, path: str): def __execute(self, parameters: List[str]) -> str: parameters.insert(0, "git") - response = subprocess.run(parameters, cwd=self.__path, stdout=subprocess.PIPE) + response = subprocess.run( + parameters, cwd=self.__path, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + if response.returncode != 0: + last_line = self.__resolve_error_message(response.stderr) + command = " ".join(parameters) + raise Exception( + f"Failed to execute: `{command}`\n\n{last_line}\n\nYou can execute the command manually to troubleshoot!" + ) return response.stdout.decode("utf-8").strip("\n") + def __resolve_error_message(self, stderr: bytes) -> str: + message = stderr.decode("utf-8").strip("\n") + return message.splitlines()[-1] + def remote(self, name: str = "origin") -> str: return self.__execute(["config", "--get", f"remote.{name}.url"]) diff --git a/tests/test_command.py b/tests/test_command.py index 632f354..754260d 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -15,10 +15,11 @@ def edit_message(message: str) -> str: return message -def aws_client_execute_side_effect(parameters, stdout) -> MagicMock: +def aws_client_execute_side_effect(parameters, stdout, stderr) -> MagicMock: data: Dict[str, Any] = {} assert -1 == stdout mock_stdout = MagicMock() + mock_stdout.returncode = 0 if "create-pull-request" in parameters: data = {"pullRequest": {"pullRequestId": "1"}} @@ -250,3 +251,42 @@ def test_invoke_no_repository_name( result = runner.invoke(main) assert result.exit_code == 1 assert "Error: The repository is not compatible with this tool!" in result.output + + +@pytest.mark.parametrize( + "remote, region, profile, config, commits, parameters", + generate_invoke_parameters([[]]), +) +@patch("pull_request_codecommit.aws.client.subprocess.run") +@patch("pull_request_codecommit.repository.GitClient") +@patch("pull_request_codecommit.click.edit") +def test_aws_failure( + mock_edit: MagicMock, + mock_git_client: MagicMock, + mock_aws_client: MagicMock, + remote: str, + region: str, + profile: str, + config: bytes, + commits: str, + parameters: List[str], +) -> None: + mock_edit.side_effect = edit_message + mock_git_client.return_value.get_commit_messages.return_value = Commits(commits) + + mock_git_client.return_value.remote.return_value = remote + mock_git_client.return_value.current_branch = "feat/my-feature" + + def side_effect(parameters, stdout, stderr): + data = {} + mock_stdout = MagicMock() + mock_stdout.stderr = bytes("Some aws failure", "utf-8") + mock_stdout.returncode = 1 + return mock_stdout + + configparser.open = MagicMock(return_value=TextIOWrapper(BytesIO(config))) # type: ignore + mock_aws_client.side_effect = side_effect + + runner = CliRunner() + result = runner.invoke(main, parameters) + assert result.exit_code == 1 diff --git a/tests/test_git_client.py b/tests/test_git_client.py index d0fee07..4bc1d03 100644 --- a/tests/test_git_client.py +++ b/tests/test_git_client.py @@ -30,11 +30,13 @@ def test_remote( ) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert path == cwd assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes(f"\n\n{remote}\n\n", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -62,11 +64,13 @@ def test_current_branch( ) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert path == cwd assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes(f"\n\n{branch}\n\n", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -91,11 +95,13 @@ def test_get_commit_messages( ) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert path == cwd assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes(COMMITS, "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -116,10 +122,12 @@ def execute(parameters, cwd, stdout): def test_get_no_commit_messages(mock_isdir: MagicMock, mock_run: MagicMock) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes("", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -134,11 +142,13 @@ def execute(parameters, cwd, stdout): def test_push(mock_isdir: MagicMock, mock_run: MagicMock) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert parameters == ["git", "push", "--set-upstream", "origin", "HEAD"] assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes("", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -150,11 +160,13 @@ def execute(parameters, cwd, stdout): def test_pull(mock_isdir: MagicMock, mock_run: MagicMock) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert parameters == ["git", "pull"] assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes("", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -166,11 +178,13 @@ def execute(parameters, cwd, stdout): def test_delete_branch(mock_isdir: MagicMock, mock_run: MagicMock) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert parameters == ["git", "branch", "-d", "feat/my-feature"] assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes("", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute @@ -182,12 +196,36 @@ def execute(parameters, cwd, stdout): def test_checkout(mock_isdir: MagicMock, mock_run: MagicMock) -> None: mock_isdir.return_value = True - def execute(parameters, cwd, stdout): + def execute(parameters, cwd, stdout, stderr): assert parameters == ["git", "checkout", "master"] assert -1 == stdout + assert -1 == stderr mock_stdout = MagicMock() mock_stdout.stdout = bytes("", "utf-8") + mock_stdout.returncode = 0 return mock_stdout mock_run.side_effect = execute Client("my-path").checkout("master") + + +@patch("pull_request_codecommit.git.client.subprocess.run") +@patch("pull_request_codecommit.git.client.os.path.isdir") +def test_git_failure(mock_isdir: MagicMock, mock_run: MagicMock) -> None: + mock_isdir.return_value = True + + def execute(parameters, cwd, stdout, stderr): + assert parameters == ["git", "checkout", "master"] + assert -1 == stdout + assert -1 == stderr + mock_stdout = MagicMock() + mock_stdout.stderr = bytes("some git failure", "utf-8") + mock_stdout.returncode = 1 + return mock_stdout + + mock_run.side_effect = execute + + with pytest.raises(Exception) as exception: + Client("my-path").checkout("master") + + assert "some git failure" in str(exception.value)