Skip to content

Commit 898a3ea

Browse files
committed
Implement fail_fast.
1 parent 94dde26 commit 898a3ea

File tree

4 files changed

+25
-3
lines changed

4 files changed

+25
-3
lines changed

pre_commit/clientlib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def validate_manifest_main(argv=None):
130130
'Config', None,
131131

132132
schema.RequiredRecurse('repos', schema.Array(CONFIG_REPO_DICT)),
133+
schema.Optional('fail_fast', schema.check_bool, False),
133134
)
134135

135136

pre_commit/commands/run.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,15 @@ def _compute_cols(hooks, verbose):
169169
return max(cols, 80)
170170

171171

172-
def _run_hooks(repo_hooks, args, environ):
172+
def _run_hooks(config, repo_hooks, args, environ):
173173
"""Actually run the hooks."""
174174
skips = _get_skips(environ)
175175
cols = _compute_cols([hook for _, hook in repo_hooks], args.verbose)
176176
retval = 0
177177
for repo, hook in repo_hooks:
178178
retval |= _run_single_hook(hook, repo, args, skips, cols)
179+
if retval and config['fail_fast']:
180+
break
179181
if (
180182
retval and
181183
args.show_diff_on_failure and
@@ -251,4 +253,4 @@ def run(runner, args, environ=os.environ):
251253
if not hook['stages'] or args.hook_stage in hook['stages']
252254
]
253255

254-
return _run_hooks(repo_hooks, args, environ)
256+
return _run_hooks(runner.config, repo_hooks, args, environ)

pre_commit/runner.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@ def git_dir(self):
3737
def config_file_path(self):
3838
return os.path.join(self.git_root, self.config_file)
3939

40+
@cached_property
41+
def config(self):
42+
return load_config(self.config_file_path)
43+
4044
@cached_property
4145
def repositories(self):
4246
"""Returns a tuple of the configured repositories."""
43-
repos = load_config(self.config_file_path)['repos']
47+
repos = self.config['repos']
4448
repos = tuple(Repository.create(x, self.store) for x in repos)
4549
for repo in repos:
4650
repo.require_installed()

tests/commands/run_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,3 +729,18 @@ def test_pass_filenames(
729729
)
730730
assert expected_out + b'\nHello World' in printed
731731
assert (b'foo.py' in printed) == pass_filenames
732+
733+
734+
def test_fail_fast(
735+
cap_out, repo_with_failing_hook, mock_out_store_directory,
736+
):
737+
with cwd(repo_with_failing_hook):
738+
with modify_config() as config:
739+
# More than one hook
740+
config['fail_fast'] = True
741+
config['repos'][0]['hooks'] *= 2
742+
stage_a_file()
743+
744+
ret, printed = _do_run(cap_out, repo_with_failing_hook, _get_opts())
745+
# it should have only run one hook
746+
assert printed.count(b'Failing hook') == 1

0 commit comments

Comments
 (0)