diff --git a/docs/configuration.rst b/docs/configuration.rst index 661e506..517e098 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -102,8 +102,15 @@ Errors and warnings ------------------- Problems detected by yamllint can be raised either as errors or as warnings. +The CLI will output them (with different colors when using the ``standard`` +output format). -In both cases, the script will output them (with different colors when using the -``standard`` output format), but the exit code can be different. More precisely, -the script will exit will a failure code *only when* there is one or more -error(s). +By default the script will exit will a return code ``1`` *only when* there is one or +more error(s). + +However if strict mode is enabled with the ``-s`` (or ``--strict``) option, the +return code will be: + + * ``0`` if no errors or warnings occur + * ``1`` if one or more errors occur + * ``2`` if no errors occur, but one or more warnings occur diff --git a/tests/test_cli.py b/tests/test_cli.py index da139a8..8a5bab5 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -40,6 +40,10 @@ class CommandLineTestCase(unittest.TestCase): '- 1 \n' '- 2') + # file with only one warning + with open(os.path.join(self.wd, 'warn.yaml'), 'w') as f: + f.write('key: value\n') + # .yml file at root open(os.path.join(self.wd, 'empty.yml'), 'w').close() @@ -85,7 +89,8 @@ class CommandLineTestCase(unittest.TestCase): [os.path.join(self.wd, 'a.yaml'), os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), - os.path.join(self.wd, 'sub/ok.yaml')], + os.path.join(self.wd, 'sub/ok.yaml'), + os.path.join(self.wd, 'warn.yaml')], ) items = [os.path.join(self.wd, 'sub/ok.yaml'), @@ -247,6 +252,24 @@ class CommandLineTestCase(unittest.TestCase): '(new-line-at-end-of-file)\n') % (file, file)) self.assertEqual(err, '') + def test_run_one_warning(self): + file = os.path.join(self.wd, 'warn.yaml') + + sys.stdout, sys.stderr = StringIO(), StringIO() + with self.assertRaises(SystemExit) as ctx: + cli.run(('-f', 'parsable', file)) + + self.assertEqual(ctx.exception.code, 0) + + def test_run_warning_in_strict_mode(self): + file = os.path.join(self.wd, 'warn.yaml') + + sys.stdout, sys.stderr = StringIO(), StringIO() + with self.assertRaises(SystemExit) as ctx: + cli.run(('-f', 'parsable', '--strict', file)) + + self.assertEqual(ctx.exception.code, 2) + def test_run_one_ok_file(self): file = os.path.join(self.wd, 'sub', 'ok.yaml') diff --git a/yamllint/cli.py b/yamllint/cli.py index 9e3e5f3..1a22f73 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -22,6 +22,7 @@ import argparse from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION from yamllint.config import YamlLintConfig, YamlLintConfigError +from yamllint.linter import PROBLEM_LEVELS from yamllint import linter @@ -85,6 +86,10 @@ def run(argv=None): parser.add_argument('-f', '--format', choices=('parsable', 'standard'), default='standard', help='format for parsing output') + parser.add_argument('-s', '--strict', + action='store_true', + help='return non-zero exit code on warnings ' + 'as well as errors') parser.add_argument('-v', '--version', action='version', version='%s %s' % (APP_NAME, APP_VERSION)) @@ -121,7 +126,7 @@ def run(argv=None): print(e, file=sys.stderr) sys.exit(-1) - return_code = 0 + max_level = 0 for file in find_files_recursively(args.files): try: @@ -143,13 +148,19 @@ def run(argv=None): print(Format.standard(problem, file)) - if return_code == 0 and problem.level == 'error': - return_code = 1 + max_level = max(max_level, PROBLEM_LEVELS[problem.level]) if not first and args.format != 'parsable': print('') except EnvironmentError as e: print(e, file=sys.stderr) - return_code = -1 + sys.exit(-1) + + if max_level == PROBLEM_LEVELS['error']: + return_code = 1 + elif max_level == PROBLEM_LEVELS['warning']: + return_code = 2 if args.strict else 0 + else: + return_code = 0 sys.exit(return_code) diff --git a/yamllint/linter.py b/yamllint/linter.py index b546e8a..1698556 100644 --- a/yamllint/linter.py +++ b/yamllint/linter.py @@ -21,6 +21,16 @@ import yaml from yamllint import parser +PROBLEM_LEVELS = { + 0: None, + 1: 'warning', + 2: 'error', + None: 0, + 'warning': 1, + 'error': 2, +} + + class LintProblem(object): """Represents a linting problem found by yamllint.""" def __init__(self, line, column, desc='', rule=None):