From 3a441b1b668b1d3f25f7768cbf8383b578293f26 Mon Sep 17 00:00:00 2001 From: Daniel Mil Date: Thu, 2 Feb 2023 14:32:07 -0800 Subject: [PATCH] fix: Return non-zero exit code on lint failures --- samcli/commands/exceptions.py | 6 ++++++ samcli/commands/validate/validate.py | 7 +++++++ .../validate/test_validate_command.py | 1 + tests/unit/commands/validate/test_cli.py | 18 ++++++++++++++++-- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/samcli/commands/exceptions.py b/samcli/commands/exceptions.py index e70ddbb9f5..d00a38ef3e 100644 --- a/samcli/commands/exceptions.py +++ b/samcli/commands/exceptions.py @@ -139,3 +139,9 @@ class InvalidStackNameException(UserException): """ Value provided to --stack-name is invalid """ + + +class LinterRuleMatchedException(UserException): + """ + The linter matched a rule meaning that the template linting failed + """ diff --git a/samcli/commands/validate/validate.py b/samcli/commands/validate/validate.py index f2f2f60181..36ad077139 100644 --- a/samcli/commands/validate/validate.py +++ b/samcli/commands/validate/validate.py @@ -12,6 +12,7 @@ from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options, print_cmdline_args from samcli.commands._utils.cdk_support_decorators import unsupported_command_cdk from samcli.commands._utils.options import template_option_without_build +from samcli.commands.exceptions import LinterRuleMatchedException from samcli.lib.telemetry.event import EventTracker from samcli.lib.telemetry.metric import track_command from samcli.cli.cli_config_file import configuration_option, TomlProvider @@ -146,12 +147,18 @@ def _lint(ctx: Context, template: str) -> None: matches = list(cfnlint.core.get_matches(filenames, args)) if not matches: click.secho("{} is a valid SAM Template".format(template), fg="green") + return + rules = cfnlint.core.get_used_rules() matches_output = formatter.print_matches(matches, rules, filenames) if matches_output: click.secho(matches_output) + raise LinterRuleMatchedException( + "Linting failed. At least one linting rule was matched to the provided template." + ) + except cfnlint.core.InvalidRegionException as e: raise UserException( "AWS Region was not found. Please configure your region through the --region option", diff --git a/tests/integration/validate/test_validate_command.py b/tests/integration/validate/test_validate_command.py index 2c931e4be7..31e85d1b75 100644 --- a/tests/integration/validate/test_validate_command.py +++ b/tests/integration/validate/test_validate_command.py @@ -162,3 +162,4 @@ def test_lint_invalid_template(self): ) self.assertIn(warning_message, output) + self.assertEqual(command_result.process.returncode, 1) diff --git a/tests/unit/commands/validate/test_cli.py b/tests/unit/commands/validate/test_cli.py index 82b6312e39..b952de467f 100644 --- a/tests/unit/commands/validate/test_cli.py +++ b/tests/unit/commands/validate/test_cli.py @@ -6,7 +6,7 @@ from cfnlint.core import CfnLintExitException, InvalidRegionException # type: ignore -from samcli.commands.exceptions import UserException +from samcli.commands.exceptions import UserException, LinterRuleMatchedException from samcli.commands.local.cli_common.user_exceptions import SamTemplateNotFoundException, InvalidSamTemplateException from samcli.commands.validate.lib.exceptions import InvalidSamDocumentException from samcli.commands.validate.validate import do_cli, _read_sam_file, _lint @@ -124,5 +124,19 @@ def test_lint_event_recorded(self, click_patch): template_path = "path_to_template" with patch("samcli.lib.telemetry.event.EventTracker.track_event") as track_patch: - _lint(ctx=ctx_lint_mock(debug=False, region="region"), template=template_path) + with self.assertRaises(LinterRuleMatchedException): + _lint(ctx=ctx_lint_mock(debug=False, region="region"), template=template_path) track_patch.assert_called_with("UsedFeature", "CFNLint") + + @patch("cfnlint.core.get_args_filenames") + @patch("cfnlint.core.get_matches") + @patch("samcli.commands.validate.validate.click") + def test_linter_raises_exception_if_matches_found(self, click_patch, matches_patch, args_patch): + template_path = "path_to_template" + args_patch.return_value = ("A", "B", Mock()) + matches_patch.return_value = ["Failed rule A", "Failed rule B"] + with self.assertRaises(LinterRuleMatchedException) as ex: + _lint(ctx=ctx_lint_mock(debug=False, region="region"), template=template_path) + self.assertEqual( + ex.exception.message, "Linting failed. At least one linting rule was matched to the provided template." + )