diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py index 474fc6e9..4ce06404 100644 --- a/pre_commit/languages/helpers.py +++ b/pre_commit/languages/helpers.py @@ -2,12 +2,12 @@ from __future__ import unicode_literals def file_args_to_stdin(file_args): - return '\n'.join(list(file_args) + ['']) + return '\0'.join(list(file_args) + ['']) def run_hook(env, hook, file_args): return env.run( - ' '.join(['xargs', hook['entry']] + hook['args']), + ' '.join(['xargs', '-0', hook['entry']] + hook['args']), stdin=file_args_to_stdin(file_args), retcode=None, ) diff --git a/pre_commit/languages/pcre.py b/pre_commit/languages/pcre.py index eebe9ac0..c66aae6d 100644 --- a/pre_commit/languages/pcre.py +++ b/pre_commit/languages/pcre.py @@ -16,7 +16,7 @@ def run_hook(repo_cmd_runner, hook, file_args): # For PCRE the entry is the regular expression to match return repo_cmd_runner.run( [ - 'xargs', 'sh', '-c', + 'xargs', '-0', 'sh', '-c', # Grep usually returns 0 for matches, and nonzero for non-matches # so we flip it here. '! grep -H -n -P {0} $@'.format(shell_escape(hook['entry'])), diff --git a/pre_commit/languages/script.py b/pre_commit/languages/script.py index 37531128..cb72e986 100644 --- a/pre_commit/languages/script.py +++ b/pre_commit/languages/script.py @@ -13,7 +13,7 @@ def install_environment(repo_cmd_runner, version='default'): def run_hook(repo_cmd_runner, hook, file_args): return repo_cmd_runner.run( - ['xargs', '{{prefix}}{0}'.format(hook['entry'])] + hook['args'], + ['xargs', '-0', '{{prefix}}{0}'.format(hook['entry'])] + hook['args'], # TODO: this is duplicated in pre_commit/languages/helpers.py stdin=file_args_to_stdin(file_args), retcode=None, diff --git a/pre_commit/languages/system.py b/pre_commit/languages/system.py index c2b65503..d7287e41 100644 --- a/pre_commit/languages/system.py +++ b/pre_commit/languages/system.py @@ -15,7 +15,7 @@ def install_environment(repo_cmd_runner, version='default'): def run_hook(repo_cmd_runner, hook, file_args): return repo_cmd_runner.run( - ['xargs'] + shlex.split(hook['entry']) + hook['args'], + ['xargs', '-0'] + shlex.split(hook['entry']) + hook['args'], stdin=file_args_to_stdin(file_args), retcode=None, ) diff --git a/testing/resources/arg_per_line_hooks_repo/bin/hook.sh b/testing/resources/arg_per_line_hooks_repo/bin/hook.sh new file mode 100755 index 00000000..47fd21df --- /dev/null +++ b/testing/resources/arg_per_line_hooks_repo/bin/hook.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +for i in "$@"; do + echo "arg: $i" +done diff --git a/testing/resources/arg_per_line_hooks_repo/hooks.yaml b/testing/resources/arg_per_line_hooks_repo/hooks.yaml new file mode 100644 index 00000000..ee1f9e9e --- /dev/null +++ b/testing/resources/arg_per_line_hooks_repo/hooks.yaml @@ -0,0 +1,5 @@ +- id: arg-per-line + name: Args per line hook + entry: bin/hook.sh + language: script + files: '' diff --git a/tests/languages/helpers_test.py b/tests/languages/helpers_test.py index b9dfdf47..8497ceb0 100644 --- a/tests/languages/helpers_test.py +++ b/tests/languages/helpers_test.py @@ -9,8 +9,8 @@ def test_file_args_to_stdin_empty(): def test_file_args_to_stdin_some(): - assert file_args_to_stdin(['foo', 'bar']) == 'foo\nbar\n' + assert file_args_to_stdin(['foo', 'bar']) == 'foo\0bar\0' def test_file_args_to_stdin_tuple(): - assert file_args_to_stdin(('foo', 'bar')) == 'foo\nbar\n' + assert file_args_to_stdin(('foo', 'bar')) == 'foo\0bar\0' diff --git a/tests/repository_test.py b/tests/repository_test.py index 4023b6c6..55950641 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -102,6 +102,14 @@ def test_run_a_script_hook(tmpdir_factory, store): ) +@pytest.mark.integration +def test_run_hook_with_spaced_args(tmpdir_factory, store): + _test_hook_repo( + tmpdir_factory, store, 'arg_per_line_hooks_repo', + 'arg-per-line', ['foo bar', 'baz'], 'arg: foo bar\narg: baz\n', + ) + + @pytest.mark.integration def test_pcre_hook_no_match(tmpdir_factory, store): path = git_dir(tmpdir_factory)