From f2644a4f2e4e1d9afe12bfb7af032284f3bddf31 Mon Sep 17 00:00:00 2001 From: Alex Hutton Date: Thu, 4 May 2017 15:45:05 +1000 Subject: [PATCH] Adds support for 'log_file' in hook config Specify a filename on a per hook basis and pre-commit will write the STDOUT and STDERR of that hook into the file. Useful for CI. Resolves #499. --- pre_commit/clientlib.py | 1 + pre_commit/commands/run.py | 5 +++- pre_commit/output.py | 19 ++++++++---- .../logfile_repo/.pre-commit-hooks.yaml | 6 ++++ testing/resources/logfile_repo/bin/hook.sh | 5 ++++ tests/commands/run_test.py | 29 +++++++++++++++++++ 6 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 testing/resources/logfile_repo/.pre-commit-hooks.yaml create mode 100755 testing/resources/logfile_repo/bin/hook.sh diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py index 23d662ac..bda5bfe3 100644 --- a/pre_commit/clientlib.py +++ b/pre_commit/clientlib.py @@ -53,6 +53,7 @@ MANIFEST_HOOK_DICT = schema.Map( '^$', ), schema.Optional('language_version', schema.check_string, 'default'), + schema.OptionalNoDefault('log_file', schema.check_string), schema.Optional('minimum_pre_commit_version', schema.check_string, '0'), schema.Optional('stages', schema.check_array(schema.check_string), []), ) diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 0ce0f383..33ea41d5 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -121,7 +121,10 @@ def _run_single_hook(hook, repo, args, skips, cols): for out in (stdout, stderr): assert type(out) is bytes, type(out) if out.strip(): - output.write_line(out.strip()) + output.write_line( + out.strip(), + logfile_name=hook.get('log_file'), + ) output.write_line() return retcode diff --git a/pre_commit/output.py b/pre_commit/output.py index b3b146f1..1fe6d513 100644 --- a/pre_commit/output.py +++ b/pre_commit/output.py @@ -71,8 +71,17 @@ def write(s, stream=stdout_byte_stream): stream.flush() -def write_line(s=None, stream=stdout_byte_stream): - if s is not None: - stream.write(five.to_bytes(s)) - stream.write(b'\n') - stream.flush() +def write_line(s=None, stream=stdout_byte_stream, logfile_name=None): + def output_streams(): + yield stream + try: + with open(logfile_name, 'ab') as logfile: + yield logfile + except (TypeError, IOError): + pass + + for output_stream in output_streams(): + if s is not None: + output_stream.write(five.to_bytes(s)) + output_stream.write(b'\n') + output_stream.flush() diff --git a/testing/resources/logfile_repo/.pre-commit-hooks.yaml b/testing/resources/logfile_repo/.pre-commit-hooks.yaml new file mode 100644 index 00000000..dcaba2e7 --- /dev/null +++ b/testing/resources/logfile_repo/.pre-commit-hooks.yaml @@ -0,0 +1,6 @@ +- id: logfile test hook + name: Logfile test hook + entry: bin/hook.sh + language: script + files: . + log_file: test.log diff --git a/testing/resources/logfile_repo/bin/hook.sh b/testing/resources/logfile_repo/bin/hook.sh new file mode 100755 index 00000000..890d9415 --- /dev/null +++ b/testing/resources/logfile_repo/bin/hook.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +echo "This is STDOUT output" +echo "This is STDERR output" 1>&2 + +exit 1 diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 5e1642e2..984ac6bd 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -211,6 +211,35 @@ def test_run( ) +def test_run_output_logfile( + cap_out, + tempdir_factory, + mock_out_store_directory, +): + + expected_output = ( + b'This is STDOUT output\n', + b'This is STDERR output\n', + ) + + git_path = make_consuming_repo(tempdir_factory, 'logfile_repo') + with cwd(git_path): + _test_run( + cap_out, + git_path, {}, + expected_output, + expected_ret=1, + stage=True + ) + logfile_path = os.path.join(git_path, 'test.log') + assert os.path.exists(logfile_path) + with open(logfile_path, 'rb') as logfile: + logfile_content = logfile.readlines() + + for expected_output_part in expected_output: + assert expected_output_part in logfile_content + + def test_always_run( cap_out, repo_with_passing_hook, mock_out_store_directory, ):