Repository: pre-commit/pre-commit

Issue:
TypeError: expected string or bytes-like object
OS: Ubuntu 19.10
App Version: pre-commit 2.1.0

Trying to create a commit-msg hook with the following hook yaml:

```yaml
---
- id: conform
  name: Conform
  description: Run 'conform enforce' for policy enforcement
  entry: conform enforce --commit-msg-file
  language: golang
  stages: [commit-msg]
```

However, when I run the `try-repo` command...

```bash
➜ pre-commit try-repo --hook-stage commit-msg .
[WARNING] Creating temporary repo with uncommitted changes...
[INFO] Initializing environment for /tmp/tmpinmhjsj2/shadow-repo.
===============================================================================
Using config:
===============================================================================
repos:
-   repo: /tmp/tmpinmhjsj2/shadow-repo
    rev: a4536d3f16eeddbe6faa39a19b3857b77967bd03
    hooks:
    -   id: conform
===============================================================================
[INFO] Installing environment for /tmp/tmpinmhjsj2/shadow-repo.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
An unexpected error has occurred: TypeError: expected string or bytes-like object
Check the log at /home/joelinux/.cache/pre-commit/pre-commit.log
```

The `pre-commit.log` file shows me this traceback:

```bash
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/error_handler.py", line 54, in error_handler
    yield
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/main.py", line 375, in main
    return try_repo(args)
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/try_repo.py", line 77, in try_repo
    return run(config_filename, store, args)
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 339, in run
    return _run_hooks(config, hooks, args, environ)
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 242, in _run_hooks
    _all_filenames(args), config['files'], config['exclude'],
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 62, in filter_by_include_exclude
    filename for filename in names
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 63, in <listcomp>
    if include_re.search(filename)
TypeError: expected string or bytes-like object
```

Looking through the code it looks like it's expecting to run the `filter_by_include_exclude()` method and it attempts to iterate over `names` which has a value of `(None,)`. Not sure if this is expected behavior but I wouldn't expect a commit-msg hook to filter any files since the only thing it should get is the filename of the commit message?

Discussion:

Patch:
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 95f8ab4..4f332ee 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -306,6 +306,15 @@ def run(
             f'`git add {config_file}` to fix this.',
         )
         return 1
+    if (
+            args.hook_stage in {'prepare-commit-msg', 'commit-msg'} and
+            not args.commit_msg_filename
+    ):
+        logger.error(
+            f'`--commit-msg-filename` is required for '
+            f'`--hook-stage {args.hook_stage}`',
+        )
+        return 1
 
     # Expose origin / source as environment variables for hooks to consume
     if args.origin and args.source:


Test patch:
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 87eef2e..4519ad1 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -663,12 +663,7 @@ def test_stages(cap_out, store, repo_with_passing_hook):
                 'language': 'pygrep',
                 'stages': [stage],
             }
-            for i, stage in enumerate(
-                (
-                    'commit', 'push', 'manual', 'prepare-commit-msg',
-                    'commit-msg',
-                ), 1,
-            )
+            for i, stage in enumerate(('commit', 'push', 'manual'), 1)
         ],
     }
     add_config_to_repo(repo_with_passing_hook, config)
@@ -686,8 +681,6 @@ def test_stages(cap_out, store, repo_with_passing_hook):
     assert _run_for_stage('commit').startswith(b'hook 1...')
     assert _run_for_stage('push').startswith(b'hook 2...')
     assert _run_for_stage('manual').startswith(b'hook 3...')
-    assert _run_for_stage('prepare-commit-msg').startswith(b'hook 4...')
-    assert _run_for_stage('commit-msg').startswith(b'hook 5...')
 
 
 def test_commit_msg_hook(cap_out, store, commit_msg_repo):
@@ -819,6 +812,16 @@ def test_error_with_unstaged_config(cap_out, store, modified_config_repo):
     assert ret == 1
 
 
+def test_commit_msg_missing_filename(cap_out, store, repo_with_passing_hook):
+    args = run_opts(hook_stage='commit-msg')
+    ret, printed = _do_run(cap_out, store, repo_with_passing_hook, args)
+    assert ret == 1
+    assert printed == (
+        b'[ERROR] `--commit-msg-filename` is required for '
+        b'`--hook-stage commit-msg`\n'
+    )
+
+
 @pytest.mark.parametrize(
     'opts', (run_opts(all_files=True), run_opts(files=[C.CONFIG_FILE])),
 )