diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py index 7d87ee043..6377a8b67 100644 --- a/pre_commit/clientlib.py +++ b/pre_commit/clientlib.py @@ -70,6 +70,7 @@ def _make_argparser(filenames_help: str) -> argparse.ArgumentParser: ), cfgv.Optional('args', cfgv.check_array(cfgv.check_string), []), cfgv.Optional('always_run', cfgv.check_bool, False), + cfgv.Optional('fail_fast', cfgv.check_bool, False), cfgv.Optional('pass_filenames', cfgv.check_bool, True), cfgv.Optional('description', cfgv.check_string, ''), cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT), diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 95ad5e964..2714faf40 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -290,7 +290,7 @@ def _run_hooks( verbose=args.verbose, use_color=args.color, ) retval |= current_retval - if retval and config['fail_fast']: + if retval and (config['fail_fast'] or hook.fail_fast): break if retval and args.show_diff_on_failure and prior_diff: if args.all_files: diff --git a/pre_commit/hook.py b/pre_commit/hook.py index ea773942b..82e99c543 100644 --- a/pre_commit/hook.py +++ b/pre_commit/hook.py @@ -27,6 +27,7 @@ class Hook(NamedTuple): additional_dependencies: Sequence[str] args: Sequence[str] always_run: bool + fail_fast: bool pass_filenames: bool description: str language_version: str diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 8c153957b..3a6fa2a10 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -985,6 +985,18 @@ def test_fail_fast(cap_out, store, repo_with_failing_hook): assert printed.count(b'Failing hook') == 1 +def test_fail_fast_per_hook(cap_out, store, repo_with_failing_hook): + with modify_config() as config: + # More than one hook + config['repos'][0]['hooks'] *= 2 + config['repos'][0]['hooks'][0]['fail_fast'] = True + stage_a_file() + + ret, printed = _do_run(cap_out, store, repo_with_failing_hook, run_opts()) + # it should have only run one hook + assert printed.count(b'Failing hook') == 1 + + def test_classifier_removes_dne(): classifier = Classifier(('this_file_does_not_exist',)) assert classifier.filenames == [] diff --git a/tests/repository_test.py b/tests/repository_test.py index 5f259070e..96c54e834 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -1002,6 +1002,7 @@ def test_manifest_hooks(tempdir_factory, store): types=['file'], types_or=[], verbose=False, + fail_fast=False, )