Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Return non-zero exit code on lint failures #4657

Merged
merged 1 commit into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions samcli/commands/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
7 changes: 7 additions & 0 deletions samcli/commands/validate/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are 0 matches, do we know that we are in exit code 1 state by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opposite actually. Line 148 checks for any matches and logs a success message if none are found

if not matches:
     click.secho("{} is a valid SAM Template".format(template), fg="green")

Otherwise there are some rules matched (meaning the linter found some issues) and we're in exit code 1 state.

"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",
Expand Down
1 change: 1 addition & 0 deletions tests/integration/validate/test_validate_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,4 @@ def test_lint_invalid_template(self):
)

self.assertIn(warning_message, output)
self.assertEqual(command_result.process.returncode, 1)
18 changes: 16 additions & 2 deletions tests/unit/commands/validate/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."
)