Repository: pre-commit/pre-commit
Pull request #1539 (2020-07-21 00:13:28)

Add --no-allow-missing-config option to init-templatedir
The hook template created by `pre-commit init-templatedir` acts like it was installed by `pre-commit install --allow-missing-config`. That behavior makes sense for most people: if you're working with some repos that have pre-commit and some that don't, it's annoying to uninstall pre-commit from the repos that don't use it. However, for people trying to adopt a policy of using pre-commit in every repo, it's easier to catch repos that are missing the config if pre-commit fails hard.

To support this special case, add a `--no-allow-missing-config` option to the `init-templatedir` command. When that option is set, create a template hook that fails in repos where the pre-commit config is missing.

asottile: I'm fine adding the option, let's remove the warning though

Patch:
diff --git a/pre_commit/commands/init_templatedir.py b/pre_commit/commands/init_templatedir.py
index f676fb1..5f17d9c 100644
--- a/pre_commit/commands/init_templatedir.py
+++ b/pre_commit/commands/init_templatedir.py
@@ -15,10 +15,15 @@ def init_templatedir(
         store: Store,
         directory: str,
         hook_types: Sequence[str],
+        skip_on_missing_config: bool = True,
 ) -> int:
     install(
-        config_file, store, hook_types=hook_types,
-        overwrite=True, skip_on_missing_config=True, git_dir=directory,
+        config_file,
+        store,
+        hook_types=hook_types,
+        overwrite=True,
+        skip_on_missing_config=skip_on_missing_config,
+        git_dir=directory,
     )
     try:
         _, out, _ = cmd_output('git', 'config', 'init.templateDir')
diff --git a/pre_commit/main.py b/pre_commit/main.py
index 874eb53..ffcc2e8 100644
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -245,6 +245,12 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
     init_templatedir_parser.add_argument(
         'directory', help='The directory in which to write the hook script.',
     )
+    init_templatedir_parser.add_argument(
+        '--no-allow-missing-config',
+        action='store_false',
+        dest='allow_missing_config',
+        help='Assume cloned repos should have a `pre-commit` config.',
+    )
     _add_hook_type_option(init_templatedir_parser)
 
     install_parser = subparsers.add_parser(
@@ -383,6 +389,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
             return init_templatedir(
                 args.config, store, args.directory,
                 hook_types=args.hook_types,
+                skip_on_missing_config=args.allow_missing_config,
             )
         elif args.command == 'install-hooks':
             return install_hooks(args.config, store)


Test patch:
diff --git a/tests/commands/init_templatedir_test.py b/tests/commands/init_templatedir_test.py
index d14a171..4e131df 100644
--- a/tests/commands/init_templatedir_test.py
+++ b/tests/commands/init_templatedir_test.py
@@ -1,6 +1,8 @@
 import os.path
 from unittest import mock
 
+import pytest
+
 import pre_commit.constants as C
 from pre_commit.commands.init_templatedir import init_templatedir
 from pre_commit.envcontext import envcontext
@@ -90,3 +92,49 @@ def test_init_templatedir_hookspath_set(tmpdir, tempdir_factory, store):
             C.CONFIG_FILE, store, target, hook_types=['pre-commit'],
         )
     assert target.join('hooks/pre-commit').exists()
+
+
+@pytest.mark.parametrize(
+    ('skip', 'commit_retcode', 'commit_output_snippet'),
+    (
+        (True, 0, 'Skipping `pre-commit`.'),
+        (False, 1, f'No {C.CONFIG_FILE} file was found'),
+    ),
+)
+def test_init_templatedir_skip_on_missing_config(
+    tmpdir,
+    tempdir_factory,
+    store,
+    cap_out,
+    skip,
+    commit_retcode,
+    commit_output_snippet,
+):
+    target = str(tmpdir.join('tmpl'))
+    init_git_dir = git_dir(tempdir_factory)
+    with cwd(init_git_dir):
+        cmd_output('git', 'config', 'init.templateDir', target)
+        init_templatedir(
+            C.CONFIG_FILE,
+            store,
+            target,
+            hook_types=['pre-commit'],
+            skip_on_missing_config=skip,
+        )
+
+    lines = cap_out.get().splitlines()
+    assert len(lines) == 1
+    assert lines[0].startswith('pre-commit installed at')
+
+    with envcontext((('GIT_TEMPLATE_DIR', target),)):
+        verify_git_dir = git_dir(tempdir_factory)
+
+    with cwd(verify_git_dir):
+        retcode, output = git_commit(
+            fn=cmd_output_mocked_pre_commit_home,
+            tempdir_factory=tempdir_factory,
+            retcode=None,
+        )
+
+        assert retcode == commit_retcode
+        assert commit_output_snippet in output
diff --git a/tests/main_test.py b/tests/main_test.py
index c472476..f7abeeb 100644
--- a/tests/main_test.py
+++ b/tests/main_test.py
@@ -159,7 +159,28 @@ def test_try_repo(mock_store_dir):
 def test_init_templatedir(mock_store_dir):
     with mock.patch.object(main, 'init_templatedir') as patch:
         main.main(('init-templatedir', 'tdir'))
+
+    assert patch.call_count == 1
+    assert 'tdir' in patch.call_args[0]
+    assert patch.call_args[1]['hook_types'] == ['pre-commit']
+    assert patch.call_args[1]['skip_on_missing_config'] is True
+
+
+def test_init_templatedir_options(mock_store_dir):
+    args = (
+        'init-templatedir',
+        'tdir',
+        '--hook-type',
+        'commit-msg',
+        '--no-allow-missing-config',
+    )
+    with mock.patch.object(main, 'init_templatedir') as patch:
+        main.main(args)
+
     assert patch.call_count == 1
+    assert 'tdir' in patch.call_args[0]
+    assert patch.call_args[1]['hook_types'] == ['commit-msg']
+    assert patch.call_args[1]['skip_on_missing_config'] is False
 
 
 def test_help_cmd_in_empty_directory(