def test_stdout_write_bug_py26( repo_with_failing_hook, mock_out_store_directory, tmpdir_factory, ): with cwd(repo_with_failing_hook): # Add bash hook on there again with io.open( '.pre-commit-config.yaml', 'a+', encoding='UTF-8', ) as config_file: config_file.write(' args: ["☃"]\n') cmd_output('git', 'add', '.pre-commit-config.yaml') stage_a_file() install(Runner(repo_with_failing_hook)) # Don't want to write to home directory env = dict(os.environ, PRE_COMMIT_HOME=tmpdir_factory.get()) # Have to use subprocess because pytest monkeypatches sys.stdout _, stdout, _ = cmd_output( 'git', 'commit', '-m', 'Commit!', # git commit puts pre-commit to stderr stderr=subprocess.STDOUT, env=env, retcode=None, ) assert 'UnicodeEncodeError' not in stdout # Doesn't actually happen, but a reasonable assertion assert 'UnicodeDecodeError' not in stdout
def test_pre_push_legacy(tempdir_factory, store): upstream = make_consuming_repo(tempdir_factory, 'script_hooks_repo') path = tempdir_factory.get() cmd_output('git', 'clone', upstream, path) with cwd(path): mkdirp(os.path.join(path, '.git/hooks')) with io.open(os.path.join(path, '.git/hooks/pre-push'), 'w') as f: f.write( '#!/usr/bin/env bash\n' 'set -eu\n' 'read lr ls rr rs\n' 'test -n "$lr" -a -n "$ls" -a -n "$rr" -a -n "$rs"\n' 'echo legacy\n', ) make_executable(f.name) install(C.CONFIG_FILE, store, hook_type='pre-push') assert _get_commit_output(tempdir_factory)[0] == 0 retc, output = _get_push_output(tempdir_factory) assert retc == 0 first_line, _, third_line = output.splitlines()[:3] assert first_line == 'legacy' assert third_line.startswith('Bash hook') assert third_line.endswith('Passed')
def test_lots_of_files(mock_out_store_directory, tempdir_factory): # windows xargs seems to have a bug, here's a regression test for # our workaround git_path = make_consuming_repo(tempdir_factory, 'python_hooks_repo') with cwd(git_path): # Override files so we run against them with modify_config() as config: config[0]['hooks'][0]['files'] = '' # Write a crap ton of files for i in range(400): filename = '{0}{1}'.format('a' * 100, i) open(filename, 'w').close() cmd_output('bash', '-c', 'git add .') install(Runner(git_path)) # Don't want to write to home directory env = dict(os.environ, PRE_COMMIT_HOME=tempdir_factory.get()) cmd_output( 'git', 'commit', '-m', 'Commit!', # git commit puts pre-commit to stderr stderr=subprocess.STDOUT, env=env, )
def test_hook_install_failure(mock_out_store_directory, tempdir_factory): git_path = make_consuming_repo(tempdir_factory, 'not_installable_repo') with cwd(git_path): install(Runner(git_path)) # Don't want to write to home directory env = dict(os.environ, PRE_COMMIT_HOME=tempdir_factory.get()) _, stdout, _ = cmd_output( 'git', 'commit', '-m', 'Commit!', # git commit puts pre-commit to stderr stderr=subprocess.STDOUT, env=env, retcode=None, encoding=None, ) assert b'UnicodeDecodeError' not in stdout # Doesn't actually happen, but a reasonable assertion assert b'UnicodeEncodeError' not in stdout # Sanity check our output assert ( b'An unexpected error has occurred: CalledProcessError: ' in stdout ) assert '☃'.encode('UTF-8') + '²'.encode('latin1') in stdout
def test_install_pre_commit(tempdir_factory): path = git_dir(tempdir_factory) runner = Runner(path, C.CONFIG_FILE) ret = install(runner) assert ret == 0 assert os.path.exists(runner.pre_commit_path) pre_commit_contents = io.open(runner.pre_commit_path).read() pre_commit_script = resource_filename('hook-tmpl') expected_contents = io.open(pre_commit_script).read().format( sys_executable=pipes.quote(sys.executable), hook_type='pre-commit', hook_specific='', config_file=runner.config_file, skip_on_missing_conf='false', ) assert pre_commit_contents == expected_contents assert os.access(runner.pre_commit_path, os.X_OK) ret = install(runner, hook_type='pre-push') assert ret == 0 assert os.path.exists(runner.pre_push_path) pre_push_contents = io.open(runner.pre_push_path).read() pre_push_tmpl = resource_filename('pre-push-tmpl') pre_push_template_contents = io.open(pre_push_tmpl).read() expected_contents = io.open(pre_commit_script).read().format( sys_executable=pipes.quote(sys.executable), hook_type='pre-push', hook_specific=pre_push_template_contents, config_file=runner.config_file, skip_on_missing_conf='false', ) assert pre_push_contents == expected_contents
def test_install_pre_commit(tmpdir_factory): path = git_dir(tmpdir_factory) runner = Runner(path) ret = install(runner) assert ret == 0 assert os.path.exists(runner.pre_commit_path) pre_commit_contents = io.open(runner.pre_commit_path).read() pre_commit_script = resource_filename('hook-tmpl') expected_contents = io.open(pre_commit_script).read().format( sys_executable=sys.executable, hook_type='pre-commit', pre_push='' ) assert pre_commit_contents == expected_contents assert os.access(runner.pre_commit_path, os.X_OK) ret = install(runner, hook_type='pre-push') assert ret == 0 assert os.path.exists(runner.pre_push_path) pre_push_contents = io.open(runner.pre_push_path).read() pre_push_tmpl = resource_filename('pre-push-tmpl') pre_push_template_contents = io.open(pre_push_tmpl).read() expected_contents = io.open(pre_commit_script).read().format( sys_executable=sys.executable, hook_type='pre-push', pre_push=pre_push_template_contents, ) assert pre_push_contents == expected_contents
def test_install_hooks_directory_not_present(tmpdir_factory): path = git_dir(tmpdir_factory) # Simulate some git clients which don't make .git/hooks #234 shutil.rmtree(os.path.join(path, '.git', 'hooks')) runner = Runner(path) install(runner) assert os.path.exists(runner.pre_commit_path)
def test_prepare_commit_msg_legacy( prepare_commit_msg_repo, tempdir_factory, store, ): hook_path = os.path.join( prepare_commit_msg_repo, '.git/hooks/prepare-commit-msg', ) mkdirp(os.path.dirname(hook_path)) with io.open(hook_path, 'w') as hook_file: hook_file.write( '#!/usr/bin/env bash\n' 'set -eu\n' 'test -e "$1"\n' 'echo legacy\n', ) make_executable(hook_path) install(C.CONFIG_FILE, store, hook_type='prepare-commit-msg') msg = 'Hi' retc, out = _get_commit_output(tempdir_factory, msg=msg) assert retc == 0 first_line, second_line = out.splitlines()[:2] assert first_line == 'legacy' assert second_line.startswith('Add "Signed off by:"...') commit_msg_path = os.path.join( prepare_commit_msg_repo, '.git/COMMIT_EDITMSG', ) with io.open(commit_msg_path) as f: assert 'Signed off by: ' in f.read()
def test_installed_from_venv(tempdir_factory): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): install(Runner(path, C.CONFIG_FILE)) # No environment so pre-commit is not on the path when running! # Should still pick up the python from when we installed ret, output = _get_commit_output( tempdir_factory, env={ 'HOME': os.path.expanduser('~'), 'PATH': _path_without_us(), 'TERM': os.environ.get('TERM', ''), # Windows needs this to import `random` 'SYSTEMROOT': os.environ.get('SYSTEMROOT', ''), # Windows needs this to resolve executables 'PATHEXT': os.environ.get('PATHEXT', ''), # Git needs this to make a commit 'GIT_AUTHOR_NAME': os.environ['GIT_AUTHOR_NAME'], 'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'], 'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'], 'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'], }, ) assert ret == 0 assert NORMAL_PRE_COMMIT_RUN.match(output)
def test_prepare_commit_msg_integration_failing( failing_prepare_commit_msg_repo, tempdir_factory, store, ): install(C.CONFIG_FILE, store, hook_type='prepare-commit-msg') retc, out = _get_commit_output(tempdir_factory) assert retc == 1 assert out.startswith('Add "Signed off by:"...') assert out.strip().endswith('...Failed')
def test_legacy_overwriting_legacy_hook(tempdir_factory, store): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): _write_legacy_hook(path) assert install(C.CONFIG_FILE, store) == 0 _write_legacy_hook(path) # this previously crashed on windows. See #1010 assert install(C.CONFIG_FILE, store) == 0
def test_commit_msg_integration_passing(commit_msg_repo, tempdir_factory): install(Runner(commit_msg_repo, C.CONFIG_FILE), hook_type='commit-msg') msg = 'Hi\nSigned off by: me, lol' retc, out = _get_commit_output(tempdir_factory, commit_msg=msg) assert retc == 0 first_line = out.splitlines()[0] assert first_line.startswith('Must have "Signed off by:"...') assert first_line.endswith('...Passed')
def test_uninstall(tmpdir_factory): path = git_dir(tmpdir_factory) runner = Runner(path) assert not os.path.exists(runner.pre_commit_path) install(runner) assert os.path.exists(runner.pre_commit_path) uninstall(runner) assert not os.path.exists(runner.pre_commit_path)
def test_install_hooks_dead_symlink( tempdir_factory, ): # pragma: no cover (non-windows) path = git_dir(tempdir_factory) os.symlink('/fake/baz', os.path.join(path, '.git', 'hooks', 'pre-commit')) runner = Runner(path) install(runner) assert os.path.exists(runner.pre_commit_path)
def test_installs_hooks_with_hooks_True(tmpdir_factory, mock_out_store_directory): path = make_consuming_repo(tmpdir_factory, "script_hooks_repo") with cwd(path): install(Runner(path), hooks=True) ret, output = _get_commit_output(tmpdir_factory, home=mock_out_store_directory) assert ret == 0 assert PRE_INSTALLED.match(output)
def test_install_hooks_directory_not_present(tempdir_factory): path = git_dir(tempdir_factory) # Simulate some git clients which don't make .git/hooks #234 hooks = os.path.join(path, '.git', 'hooks') if os.path.exists(hooks): # pragma: no cover (latest git) shutil.rmtree(hooks) runner = Runner(path) install(runner) assert os.path.exists(runner.pre_commit_path)
def test_install_idempotent(tmpdir_factory): path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo') with cwd(path): assert install(Runner(path)) == 0 assert install(Runner(path)) == 0 ret, output = _get_commit_output(tmpdir_factory) assert ret == 0 assert NORMAL_PRE_COMMIT_RUN.match(output)
def test_installs_hooks_with_hooks_True(tempdir_factory, store): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): install(C.CONFIG_FILE, store, hooks=True) ret, output = _get_commit_output( tempdir_factory, pre_commit_home=store.directory, ) assert ret == 0 assert PRE_INSTALLED.match(output)
def test_pre_push_integration_empty_push(tempdir_factory): upstream = make_consuming_repo(tempdir_factory, 'script_hooks_repo') path = tempdir_factory.get() cmd_output('git', 'clone', upstream, path) with cwd(path): install(Runner(path), hook_type='pre-push') _get_push_output(tempdir_factory) retc, output = _get_push_output(tempdir_factory) assert output == 'Everything up-to-date\n' assert retc == 0
def test_non_ascii_hook_id( repo_with_passing_hook, mock_out_store_directory, tempdir_factory, ): with cwd(repo_with_passing_hook): install(Runner(repo_with_passing_hook, C.CONFIG_FILE)) _, stdout, _ = cmd_output_mocked_pre_commit_home( sys.executable, '-m', 'pre_commit.main', 'run', '☃', retcode=None, tempdir_factory=tempdir_factory, ) assert 'UnicodeDecodeError' not in stdout # Doesn't actually happen, but a reasonable assertion assert 'UnicodeEncodeError' not in stdout
def test_install_hooks_command(tempdir_factory, mock_out_store_directory): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): runner = Runner(path, C.CONFIG_FILE) install(runner) install_hooks(runner) ret, output = _get_commit_output( tempdir_factory, pre_commit_home=mock_out_store_directory, ) assert ret == 0 assert PRE_INSTALLED.match(output)
def test_pre_push_integration_accepted(tmpdir_factory): upstream = make_consuming_repo(tmpdir_factory, 'script_hooks_repo') path = tmpdir_factory.get() cmd_output('git', 'clone', upstream, path) with cwd(path): install(Runner(path), hook_type='pre-push') assert _get_commit_output(tmpdir_factory)[0] == 0 retc, output = _get_push_output(tmpdir_factory) assert retc == 0 assert 'Bash hook' in output assert 'Passed' in output
def test_pre_push_integration_accepted(tmpdir_factory): upstream = make_consuming_repo(tmpdir_factory, "script_hooks_repo") path = tmpdir_factory.get() cmd_output("git", "clone", upstream, path) with cwd(path): install(Runner(path), hook_type="pre-push") assert _get_commit_output(tmpdir_factory)[0] == 0 retc, output = _get_push_output(tmpdir_factory) assert retc == 0 assert "Bash hook" in output assert "Passed" in output
def test_non_ascii_hook_id( repo_with_passing_hook, mock_out_store_directory, tempdir_factory, ): with cwd(repo_with_passing_hook): install(Runner(repo_with_passing_hook)) # Don't want to write to home directory env = dict(os.environ, PRE_COMMIT_HOME=tempdir_factory.get()) _, stdout, _ = cmd_output( sys.executable, '-m', 'pre_commit.main', 'run', '☃', env=env, retcode=None, ) assert 'UnicodeDecodeError' not in stdout # Doesn't actually happen, but a reasonable assertion assert 'UnicodeEncodeError' not in stdout
def test_install_existing_hook_no_overwrite_idempotent(tempdir_factory, store): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): _write_legacy_hook(path) # Install twice assert install(C.CONFIG_FILE, store) == 0 assert install(C.CONFIG_FILE, store) == 0 # We should run both the legacy and pre-commit hooks ret, output = _get_commit_output(tempdir_factory) assert ret == 0 assert output.startswith('legacy hook\n') assert NORMAL_PRE_COMMIT_RUN.match(output[len('legacy hook\n'):])
def test_pre_push_integration_failing(tmpdir_factory): upstream = make_consuming_repo(tmpdir_factory, 'failing_hook_repo') path = tmpdir_factory.get() cmd_output('git', 'clone', upstream, path) with cwd(path): install(Runner(path), hook_type='pre-push') # commit succeeds because pre-commit is only installed for pre-push assert _get_commit_output(tmpdir_factory)[0] == 0 retc, output = _get_push_output(tmpdir_factory) assert retc == 1 assert 'Failing hook' in output assert 'Failed' in output assert 'hookid: failing_hook' in output
def test_pre_push_integration_failing(tmpdir_factory): upstream = make_consuming_repo(tmpdir_factory, "failing_hook_repo") path = tmpdir_factory.get() cmd_output("git", "clone", upstream, path) with cwd(path): install(Runner(path), hook_type="pre-push") # commit succeeds because pre-commit is only installed for pre-push assert _get_commit_output(tmpdir_factory)[0] == 0 retc, output = _get_push_output(tmpdir_factory) assert retc == 1 assert "Failing hook" in output assert "Failed" in output assert "hookid: failing_hook" in output
def test_installed_from_venv(tmpdir_factory): path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo') with local.cwd(path): install(Runner(path)) # No environment so pre-commit is not on the path when running! # Should still pick up the python from when we installed ret, output = _get_commit_output( tmpdir_factory, env_base={ 'HOME': os.environ['HOME'], 'TERM': os.environ.get('TERM', ''), }, ) assert ret == 0 assert NORMAL_PRE_COMMIT_RUN.match(output)
def test_pre_push_new_upstream(tempdir_factory, store): upstream = make_consuming_repo(tempdir_factory, 'script_hooks_repo') upstream2 = git_dir(tempdir_factory) path = tempdir_factory.get() cmd_output('git', 'clone', upstream, path) with cwd(path): install(C.CONFIG_FILE, store, hook_type='pre-push') assert _get_commit_output(tempdir_factory)[0] == 0 cmd_output('git', 'remote', 'rename', 'origin', 'upstream') cmd_output('git', 'remote', 'add', 'origin', upstream2) retc, output = _get_push_output(tempdir_factory) assert retc == 0 assert 'Bash hook' in output assert 'Passed' in output
def test_prepare_commit_msg_integration_passing( prepare_commit_msg_repo, tempdir_factory, store, ): install(C.CONFIG_FILE, store, hook_type='prepare-commit-msg') msg = 'Hi' retc, out = _get_commit_output(tempdir_factory, msg=msg) assert retc == 0 first_line = out.splitlines()[0] assert first_line.startswith('Add "Signed off by:"...') assert first_line.endswith('...Passed') commit_msg_path = os.path.join( prepare_commit_msg_repo, '.git/COMMIT_EDITMSG', ) with io.open(commit_msg_path) as f: assert 'Signed off by: ' in f.read()
def test_replace_old_commit_script(tempdir_factory): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): runner = Runner(path, C.CONFIG_FILE) # Install a script that looks like our old script pre_commit_contents = io.open( resource_filename('hook-tmpl'), ).read() new_contents = pre_commit_contents.replace( CURRENT_HASH, PRIOR_HASHES[-1], ) mkdirp(os.path.dirname(runner.pre_commit_path)) with io.open(runner.pre_commit_path, 'w') as pre_commit_file: pre_commit_file.write(new_contents) make_executable(runner.pre_commit_path) # Install normally assert install(runner) == 0 ret, output = _get_commit_output(tempdir_factory) assert ret == 0 assert NORMAL_PRE_COMMIT_RUN.match(output)
def test_pre_merge_commit_integration(tempdir_factory, store): output_pattern = re_assert.Matches( r'^\[INFO\] Initializing environment for .+\n' r'Bash hook\.+Passed\n' r"Merge made by the 'recursive' strategy.\n" r' foo \| 0\n' r' 1 file changed, 0 insertions\(\+\), 0 deletions\(-\)\n' r' create mode 100644 foo\n$', ) path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): ret = install(C.CONFIG_FILE, store, hook_types=['pre-merge-commit']) assert ret == 0 cmd_output('git', 'checkout', 'master', '-b', 'feature') _get_commit_output(tempdir_factory) cmd_output('git', 'checkout', 'master') ret, output, _ = cmd_output_mocked_pre_commit_home( 'git', 'merge', '--no-ff', '--no-edit', 'feature', tempdir_factory=tempdir_factory, ) assert ret == 0 output_pattern.assert_matches(output)
def test_install_existing_hooks_no_overwrite(tempdir_factory): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): runner = Runner(path) # Write out an "old" hook mkdirp(os.path.dirname(runner.pre_commit_path)) with io.open(runner.pre_commit_path, 'w') as hook_file: hook_file.write('#!/usr/bin/env bash\necho "legacy hook"\n') make_executable(runner.pre_commit_path) # Make sure we installed the "old" hook correctly ret, output = _get_commit_output(tempdir_factory, touch_file='baz') assert ret == 0 assert EXISTING_COMMIT_RUN.match(output) # Now install pre-commit (no-overwrite) assert install(runner) == 0 # We should run both the legacy and pre-commit hooks ret, output = _get_commit_output(tempdir_factory) assert ret == 0 assert output.startswith('legacy hook\n') assert NORMAL_PRE_COMMIT_RUN.match(output[len('legacy hook\n'):])
def test_environment_not_sourced(tempdir_factory, store): path = make_consuming_repo(tempdir_factory, 'script_hooks_repo') with cwd(path): # Patch the executable to simulate rming virtualenv with mock.patch.object(sys, 'executable', '/does-not-exist'): assert not install(C.CONFIG_FILE, store, hook_types=['pre-commit']) # Use a specific homedir to ignore --user installs homedir = tempdir_factory.get() ret, out = git_commit( env={ 'HOME': homedir, 'PATH': _path_without_us(), # Git needs this to make a commit 'GIT_AUTHOR_NAME': os.environ['GIT_AUTHOR_NAME'], 'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'], 'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'], 'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'], }, retcode=None, ) assert ret == 1 assert out == ('`pre-commit` not found. ' 'Did you forget to activate your virtualenv?\n')
def test_install_refuses_core_hookspath(tempdir_factory, store): path = git_dir(tempdir_factory) with cwd(path): cmd_output('git', 'config', '--local', 'core.hooksPath', 'hooks') runner = Runner(path, C.CONFIG_FILE) assert install(runner, store)
def main(argv=None): argv = argv if argv is not None else sys.argv[1:] argv = [five.to_text(arg) for arg in argv] parser = argparse.ArgumentParser() # http://stackoverflow.com/a/8521644/812183 parser.add_argument( '-V', '--version', action='version', version='%(prog)s {}'.format(C.VERSION), ) subparsers = parser.add_subparsers(dest='command') install_parser = subparsers.add_parser( 'install', help='Install the pre-commit script.', ) _add_color_option(install_parser) _add_config_option(install_parser) install_parser.add_argument( '-f', '--overwrite', action='store_true', help='Overwrite existing hooks / remove migration mode.', ) install_parser.add_argument( '--install-hooks', action='store_true', help=('Whether to install hook environments for all environments ' 'in the config file.'), ) install_parser.add_argument( '-t', '--hook-type', choices=('pre-commit', 'pre-push'), default='pre-commit', ) install_parser.add_argument( '--allow-missing-config', action='store_true', default=False, help=('Whether to allow a missing `pre-config` configuration file ' 'or exit with a failure code.'), ) install_hooks_parser = subparsers.add_parser( 'install-hooks', help=('Install hook environments for all environments in the config ' 'file. You may find `pre-commit install --install-hooks` more ' 'useful.'), ) _add_color_option(install_hooks_parser) _add_config_option(install_hooks_parser) uninstall_parser = subparsers.add_parser( 'uninstall', help='Uninstall the pre-commit script.', ) _add_color_option(uninstall_parser) _add_config_option(uninstall_parser) uninstall_parser.add_argument( '-t', '--hook-type', choices=('pre-commit', 'pre-push'), default='pre-commit', ) clean_parser = subparsers.add_parser( 'clean', help='Clean out pre-commit files.', ) _add_color_option(clean_parser) _add_config_option(clean_parser) autoupdate_parser = subparsers.add_parser( 'autoupdate', help="Auto-update pre-commit config to the latest repos' versions.", ) _add_color_option(autoupdate_parser) _add_config_option(autoupdate_parser) autoupdate_parser.add_argument( '--tags-only', action='store_true', help='Update to tags only.', ) run_parser = subparsers.add_parser('run', help='Run hooks.') _add_color_option(run_parser) _add_config_option(run_parser) run_parser.add_argument('hook', nargs='?', help='A single hook-id to run') run_parser.add_argument( '--no-stash', default=False, action='store_true', help='Use this option to prevent auto stashing of unstaged files.', ) run_parser.add_argument( '--verbose', '-v', action='store_true', default=False, ) run_parser.add_argument( '--origin', '-o', help="The origin branch's commit_id when using `git push`.", ) run_parser.add_argument( '--source', '-s', help="The remote branch's commit_id when using `git push`.", ) run_parser.add_argument( '--allow-unstaged-config', default=False, action='store_true', help=('Allow an unstaged config to be present. Note that this will ' 'be stashed before parsing unless --no-stash is specified.'), ) run_parser.add_argument( '--hook-stage', choices=('commit', 'push'), default='commit', help='The stage during which the hook is fired e.g. commit or push.', ) run_parser.add_argument( '--show-diff-on-failure', action='store_true', help='When hooks fail, run `git diff` directly afterward.', ) run_mutex_group = run_parser.add_mutually_exclusive_group(required=False) run_mutex_group.add_argument( '--all-files', '-a', action='store_true', default=False, help='Run on all the files in the repo. Implies --no-stash.', ) run_mutex_group.add_argument( '--files', nargs='*', default=[], help='Specific filenames to run hooks on.', ) help = subparsers.add_parser( 'help', help='Show help for a specific command.', ) help.add_argument('help_cmd', nargs='?', help='Command to show help for.') # Argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ['run'] args = parser.parse_args(argv) if args.command == 'run': args.files = [ os.path.relpath(os.path.abspath(filename), git.get_root()) for filename in args.files ] if args.command == 'help': if args.help_cmd: parser.parse_args([args.help_cmd, '--help']) else: parser.parse_args(['--help']) with error_handler(): add_logging_handler(args.color) runner = Runner.create(args.config) git.check_for_cygwin_mismatch() if args.command == 'install': return install( runner, overwrite=args.overwrite, hooks=args.install_hooks, hook_type=args.hook_type, skip_on_missing_conf=args.allow_missing_config, ) elif args.command == 'install-hooks': return install_hooks(runner) elif args.command == 'uninstall': return uninstall(runner, hook_type=args.hook_type) elif args.command == 'clean': return clean(runner) elif args.command == 'autoupdate': return autoupdate(runner, args.tags_only) elif args.command == 'run': return run(runner, args) else: raise NotImplementedError('Command {} not implemented.'.format( args.command)) raise AssertionError( 'Command {} failed to exit with a returncode'.format(args.command))
def main(argv=None): argv = argv if argv is not None else sys.argv[1:] parser = argparse.ArgumentParser() # http://stackoverflow.com/a/8521644/812183 parser.add_argument( '-V', '--version', action='version', version='%(prog)s {0}'.format( pkg_resources.get_distribution('pre-commit').version)) subparsers = parser.add_subparsers(dest='command') install_parser = subparsers.add_parser( 'install', help='Install the pre-commit script.', ) install_parser.add_argument( '-f', '--overwrite', action='store_true', help='Overwrite existing hooks / remove migration mode.', ) install_parser.add_argument( '--install-hooks', action='store_true', help=('Whether to install hook environments for all environments ' 'in the config file.'), ) subparsers.add_parser('uninstall', help='Uninstall the pre-commit script.') subparsers.add_parser('clean', help='Clean out pre-commit files.') subparsers.add_parser( 'autoupdate', help="Auto-update pre-commit config to the latest repos' versions.", ) run_parser = subparsers.add_parser('run', help='Run hooks.') run_parser.add_argument('hook', nargs='?', help='A single hook-id to run') run_parser.add_argument( '--color', default='auto', type=color.use_color, help='Whether to use color in output. Defaults to `auto`', ) run_parser.add_argument( '--no-stash', default=False, action='store_true', help='Use this option to prevent auto stashing of unstaged files.', ) run_parser.add_argument( '--verbose', '-v', action='store_true', default=False, ) run_mutex_group = run_parser.add_mutually_exclusive_group(required=False) run_mutex_group.add_argument( '--all-files', '-a', action='store_true', default=False, help='Run on all the files in the repo. Implies --no-stash.', ) run_mutex_group.add_argument( '--files', nargs='*', help='Specific filenames to run hooks on.', ) help = subparsers.add_parser('help', help='Show help for a specific command.') help.add_argument('help_cmd', nargs='?', help='Command to show help for.') # Argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ['run'] args = parser.parse_args(argv) if args.command == 'help': if args.help_cmd: parser.parse_args([args.help_cmd, '--help']) else: parser.parse_args(['--help']) with error_handler(): runner = Runner.create() if args.command == 'install': return install( runner, overwrite=args.overwrite, hooks=args.install_hooks, ) elif args.command == 'uninstall': return uninstall(runner) elif args.command == 'clean': return clean(runner) elif args.command == 'autoupdate': return autoupdate(runner) elif args.command == 'run': return run(runner, args) else: raise NotImplementedError('Command {0} not implemented.'.format( args.command)) raise AssertionError( 'Command {0} failed to exit with a returncode'.format( args.command))
def main(argv: Sequence[str] | None = None) -> int: argv = argv if argv is not None else sys.argv[1:] parser = argparse.ArgumentParser(prog='pre-commit') # https://stackoverflow.com/a/8521644/812183 parser.add_argument( '-V', '--version', action='version', version=f'%(prog)s {C.VERSION}', ) subparsers = parser.add_subparsers(dest='command') def _add_cmd(name: str, *, help: str) -> argparse.ArgumentParser: parser = subparsers.add_parser(name, help=help) add_color_option(parser) return parser autoupdate_parser = _add_cmd( 'autoupdate', help="Auto-update pre-commit config to the latest repos' versions.", ) _add_config_option(autoupdate_parser) autoupdate_parser.add_argument( '--bleeding-edge', action='store_true', help=( 'Update to the bleeding edge of `HEAD` instead of the latest ' 'tagged version (the default behavior).' ), ) autoupdate_parser.add_argument( '--freeze', action='store_true', help='Store "frozen" hashes in `rev` instead of tag names', ) autoupdate_parser.add_argument( '--repo', dest='repos', action='append', metavar='REPO', help='Only update this repository -- may be specified multiple times.', ) _add_cmd('clean', help='Clean out pre-commit files.') _add_cmd('gc', help='Clean unused cached repos.') init_templatedir_parser = _add_cmd( 'init-templatedir', help=( 'Install hook script in a directory intended for use with ' '`git config init.templateDir`.' ), ) _add_config_option(init_templatedir_parser) 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 = _add_cmd('install', help='Install the pre-commit script.') _add_config_option(install_parser) install_parser.add_argument( '-f', '--overwrite', action='store_true', help='Overwrite existing hooks / remove migration mode.', ) install_parser.add_argument( '--install-hooks', action='store_true', help=( 'Whether to install hook environments for all environments ' 'in the config file.' ), ) _add_hook_type_option(install_parser) install_parser.add_argument( '--allow-missing-config', action='store_true', default=False, help=( 'Whether to allow a missing `pre-commit` configuration file ' 'or exit with a failure code.' ), ) install_hooks_parser = _add_cmd( 'install-hooks', help=( 'Install hook environments for all environments in the config ' 'file. You may find `pre-commit install --install-hooks` more ' 'useful.' ), ) _add_config_option(install_hooks_parser) migrate_config_parser = _add_cmd( 'migrate-config', help='Migrate list configuration to new map configuration.', ) _add_config_option(migrate_config_parser) run_parser = _add_cmd('run', help='Run hooks.') _add_config_option(run_parser) _add_run_options(run_parser) _add_cmd('sample-config', help=f'Produce a sample {C.CONFIG_FILE} file') try_repo_parser = _add_cmd( 'try-repo', help='Try the hooks in a repository, useful for developing new hooks.', ) _add_config_option(try_repo_parser) try_repo_parser.add_argument( 'repo', help='Repository to source hooks from.', ) try_repo_parser.add_argument( '--ref', '--rev', help=( 'Manually select a rev to run against, otherwise the `HEAD` ' 'revision will be used.' ), ) _add_run_options(try_repo_parser) uninstall_parser = _add_cmd( 'uninstall', help='Uninstall the pre-commit script.', ) _add_config_option(uninstall_parser) _add_hook_type_option(uninstall_parser) validate_config_parser = _add_cmd( 'validate-config', help='Validate .pre-commit-config.yaml files', ) validate_config_parser.add_argument('filenames', nargs='*') validate_manifest_parser = _add_cmd( 'validate-manifest', help='Validate .pre-commit-hooks.yaml files', ) validate_manifest_parser.add_argument('filenames', nargs='*') # does not use `_add_cmd` because it doesn't use `--color` help = subparsers.add_parser( 'help', help='Show help for a specific command.', ) help.add_argument('help_cmd', nargs='?', help='Command to show help for.') # not intended for users to call this directly hook_impl_parser = subparsers.add_parser('hook-impl') add_color_option(hook_impl_parser) _add_config_option(hook_impl_parser) hook_impl_parser.add_argument('--hook-type') hook_impl_parser.add_argument('--hook-dir') hook_impl_parser.add_argument( '--skip-on-missing-config', action='store_true', ) hook_impl_parser.add_argument(dest='rest', nargs=argparse.REMAINDER) # argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ['run'] args = parser.parse_args(argv) if args.command == 'help' and args.help_cmd: parser.parse_args([args.help_cmd, '--help']) elif args.command == 'help': parser.parse_args(['--help']) with error_handler(), logging_handler(args.color): git.check_for_cygwin_mismatch() store = Store() if args.command not in COMMANDS_NO_GIT: _adjust_args_and_chdir(args) store.mark_config_used(args.config) if args.command == 'autoupdate': return autoupdate( args.config, store, tags_only=not args.bleeding_edge, freeze=args.freeze, repos=args.repos, ) elif args.command == 'clean': return clean(store) elif args.command == 'gc': return gc(store) elif args.command == 'hook-impl': return hook_impl( store, config=args.config, color=args.color, hook_type=args.hook_type, hook_dir=args.hook_dir, skip_on_missing_config=args.skip_on_missing_config, args=args.rest[1:], ) elif args.command == 'install': return install( args.config, store, hook_types=args.hook_types, overwrite=args.overwrite, hooks=args.install_hooks, skip_on_missing_config=args.allow_missing_config, ) elif args.command == 'init-templatedir': 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) elif args.command == 'migrate-config': return migrate_config(args.config) elif args.command == 'run': return run(args.config, store, args) elif args.command == 'sample-config': return sample_config() elif args.command == 'try-repo': return try_repo(args) elif args.command == 'uninstall': return uninstall( config_file=args.config, hook_types=args.hook_types, ) elif args.command == 'validate-config': return validate_config(args.filenames) elif args.command == 'validate-manifest': return validate_manifest(args.filenames) else: raise NotImplementedError( f'Command {args.command} not implemented.', ) raise AssertionError( f'Command {args.command} failed to exit with a returncode', )
def test_commit_msg_integration_failing(commit_msg_repo, tempdir_factory): install(Runner(commit_msg_repo, C.CONFIG_FILE), hook_type='commit-msg') retc, out = _get_commit_output(tempdir_factory) assert retc == 1 assert out.startswith('Must have "Signed off by:"...') assert out.strip().endswith('...Failed')
def test_uninstall(in_git_dir, store): assert not in_git_dir.join('.git/hooks/pre-commit').exists() install(C.CONFIG_FILE, store, hook_types=['pre-commit']) assert in_git_dir.join('.git/hooks/pre-commit').exists() uninstall(hook_types=['pre-commit']) assert not in_git_dir.join('.git/hooks/pre-commit').exists()
def main(argv: Optional[Sequence[str]] = None) -> int: argv = argv if argv is not None else sys.argv[1:] parser = argparse.ArgumentParser(prog="pre-commit") # https://stackoverflow.com/a/8521644/812183 parser.add_argument( "-V", "--version", action="version", version=f"%(prog)s {C.VERSION}", ) subparsers = parser.add_subparsers(dest="command") autoupdate_parser = subparsers.add_parser( "autoupdate", help="Auto-update pre-commit config to the latest repos' versions.", ) _add_color_option(autoupdate_parser) _add_config_option(autoupdate_parser) autoupdate_parser.add_argument( "--bleeding-edge", action="store_true", help=("Update to the bleeding edge of `master` instead of the latest " "tagged version (the default behavior)."), ) autoupdate_parser.add_argument( "--freeze", action="store_true", help='Store "frozen" hashes in `rev` instead of tag names', ) autoupdate_parser.add_argument( "--repo", dest="repos", action="append", metavar="REPO", help="Only update this repository -- may be specified multiple times.", ) clean_parser = subparsers.add_parser( "clean", help="Clean out pre-commit files.", ) _add_color_option(clean_parser) _add_config_option(clean_parser) hook_impl_parser = subparsers.add_parser("hook-impl") _add_color_option(hook_impl_parser) _add_config_option(hook_impl_parser) hook_impl_parser.add_argument("--hook-type") hook_impl_parser.add_argument("--hook-dir") hook_impl_parser.add_argument( "--skip-on-missing-config", action="store_true", ) hook_impl_parser.add_argument(dest="rest", nargs=argparse.REMAINDER) gc_parser = subparsers.add_parser("gc", help="Clean unused cached repos.") _add_color_option(gc_parser) _add_config_option(gc_parser) init_templatedir_parser = subparsers.add_parser( "init-templatedir", help=("Install hook script in a directory intended for use with " "`git config init.templateDir`."), ) _add_color_option(init_templatedir_parser) _add_config_option(init_templatedir_parser) init_templatedir_parser.add_argument( "directory", help="The directory in which to write the hook script.", ) _add_hook_type_option(init_templatedir_parser) install_parser = subparsers.add_parser( "install", help="Install the pre-commit script.", ) _add_color_option(install_parser) _add_config_option(install_parser) install_parser.add_argument( "-f", "--overwrite", action="store_true", help="Overwrite existing hooks / remove migration mode.", ) install_parser.add_argument( "--install-hooks", action="store_true", help=("Whether to install hook environments for all environments " "in the config file."), ) _add_hook_type_option(install_parser) install_parser.add_argument( "--allow-missing-config", action="store_true", default=False, help=("Whether to allow a missing `pre-commit` configuration file " "or exit with a failure code."), ) install_hooks_parser = subparsers.add_parser( "install-hooks", help=("Install hook environments for all environments in the config " "file. You may find `pre-commit install --install-hooks` more " "useful."), ) _add_color_option(install_hooks_parser) _add_config_option(install_hooks_parser) migrate_config_parser = subparsers.add_parser( "migrate-config", help="Migrate list configuration to new map configuration.", ) _add_color_option(migrate_config_parser) _add_config_option(migrate_config_parser) run_parser = subparsers.add_parser("run", help="Run hooks.") _add_color_option(run_parser) _add_config_option(run_parser) _add_run_options(run_parser) sample_config_parser = subparsers.add_parser( "sample-config", help=f"Produce a sample {C.CONFIG_FILE} file", ) _add_color_option(sample_config_parser) _add_config_option(sample_config_parser) try_repo_parser = subparsers.add_parser( "try-repo", help="Try the hooks in a repository, useful for developing new hooks.", ) _add_color_option(try_repo_parser) _add_config_option(try_repo_parser) try_repo_parser.add_argument( "repo", help="Repository to source hooks from.", ) try_repo_parser.add_argument( "--ref", "--rev", help=("Manually select a rev to run against, otherwise the `HEAD` " "revision will be used."), ) _add_run_options(try_repo_parser) uninstall_parser = subparsers.add_parser( "uninstall", help="Uninstall the pre-commit script.", ) _add_color_option(uninstall_parser) _add_config_option(uninstall_parser) _add_hook_type_option(uninstall_parser) help = subparsers.add_parser( "help", help="Show help for a specific command.", ) help.add_argument("help_cmd", nargs="?", help="Command to show help for.") # argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ["run"] args = parser.parse_args(argv) if args.command == "help" and args.help_cmd: parser.parse_args([args.help_cmd, "--help"]) elif args.command == "help": parser.parse_args(["--help"]) with error_handler(), logging_handler(args.color): if args.command not in COMMANDS_NO_GIT: _adjust_args_and_chdir(args) git.check_for_cygwin_mismatch() store = Store() store.mark_config_used(args.config) if args.command == "autoupdate": return autoupdate( args.config, store, tags_only=not args.bleeding_edge, freeze=args.freeze, repos=args.repos, ) elif args.command == "clean": return clean(store) elif args.command == "gc": return gc(store) elif args.command == "hook-impl": return hook_impl( store, config=args.config, color=args.color, hook_type=args.hook_type, hook_dir=args.hook_dir, skip_on_missing_config=args.skip_on_missing_config, args=args.rest[1:], ) elif args.command == "install": return install( args.config, store, hook_types=args.hook_types, overwrite=args.overwrite, hooks=args.install_hooks, skip_on_missing_config=args.allow_missing_config, ) elif args.command == "init-templatedir": return init_templatedir( args.config, store, args.directory, hook_types=args.hook_types, ) elif args.command == "install-hooks": return install_hooks(args.config, store) elif args.command == "migrate-config": return migrate_config(args.config) elif args.command == "run": return run(args.config, store, args) elif args.command == "sample-config": return sample_config() elif args.command == "try-repo": return try_repo(args) elif args.command == "uninstall": return uninstall(hook_types=args.hook_types) else: raise NotImplementedError( f"Command {args.command} not implemented.", ) raise AssertionError( f"Command {args.command} failed to exit with a returncode", )
def main(argv=None): argv = argv if argv is not None else sys.argv[1:] argv = [five.to_text(arg) for arg in argv] parser = argparse.ArgumentParser() # http://stackoverflow.com/a/8521644/812183 parser.add_argument( '-V', '--version', action='version', version='%(prog)s {}'.format(C.VERSION), ) subparsers = parser.add_subparsers(dest='command') install_parser = subparsers.add_parser( 'install', help='Install the pre-commit script.', ) _add_color_option(install_parser) _add_config_option(install_parser) install_parser.add_argument( '-f', '--overwrite', action='store_true', help='Overwrite existing hooks / remove migration mode.', ) install_parser.add_argument( '--install-hooks', action='store_true', help=('Whether to install hook environments for all environments ' 'in the config file.'), ) _add_hook_type_option(install_parser) install_parser.add_argument( '--allow-missing-config', action='store_true', default=False, help=('Whether to allow a missing `pre-commit` configuration file ' 'or exit with a failure code.'), ) install_hooks_parser = subparsers.add_parser( 'install-hooks', help=('Install hook environments for all environments in the config ' 'file. You may find `pre-commit install --install-hooks` more ' 'useful.'), ) _add_color_option(install_hooks_parser) _add_config_option(install_hooks_parser) uninstall_parser = subparsers.add_parser( 'uninstall', help='Uninstall the pre-commit script.', ) _add_color_option(uninstall_parser) _add_config_option(uninstall_parser) _add_hook_type_option(uninstall_parser) clean_parser = subparsers.add_parser( 'clean', help='Clean out pre-commit files.', ) _add_color_option(clean_parser) _add_config_option(clean_parser) autoupdate_parser = subparsers.add_parser( 'autoupdate', help="Auto-update pre-commit config to the latest repos' versions.", ) _add_color_option(autoupdate_parser) _add_config_option(autoupdate_parser) autoupdate_parser.add_argument( '--tags-only', action='store_true', help='LEGACY: for compatibility', ) autoupdate_parser.add_argument( '--bleeding-edge', action='store_true', help=('Update to the bleeding edge of `master` instead of the latest ' 'tagged version (the default behavior).'), ) migrate_config_parser = subparsers.add_parser( 'migrate-config', help='Migrate list configuration to new map configuration.', ) _add_color_option(migrate_config_parser) _add_config_option(migrate_config_parser) run_parser = subparsers.add_parser('run', help='Run hooks.') _add_color_option(run_parser) _add_config_option(run_parser) _add_run_options(run_parser) sample_config_parser = subparsers.add_parser( 'sample-config', help='Produce a sample {} file'.format(C.CONFIG_FILE), ) _add_color_option(sample_config_parser) _add_config_option(sample_config_parser) try_repo_parser = subparsers.add_parser( 'try-repo', help='Try the hooks in a repository, useful for developing new hooks.', ) _add_color_option(try_repo_parser) _add_config_option(try_repo_parser) try_repo_parser.add_argument( 'repo', help='Repository to source hooks from.', ) try_repo_parser.add_argument( '--ref', help=('Manually select a ref to run against, otherwise the `HEAD` ' 'revision will be used.'), ) _add_run_options(try_repo_parser) help = subparsers.add_parser( 'help', help='Show help for a specific command.', ) help.add_argument('help_cmd', nargs='?', help='Command to show help for.') # Argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ['run'] args = parser.parse_args(argv) if args.command == 'run': args.files = [ os.path.relpath(os.path.abspath(filename), git.get_root()) for filename in args.files ] if args.command == 'help': if args.help_cmd: parser.parse_args([args.help_cmd, '--help']) else: parser.parse_args(['--help']) with error_handler(): add_logging_handler(args.color) runner = Runner.create(args.config) git.check_for_cygwin_mismatch() if args.command == 'install': return install( runner, overwrite=args.overwrite, hooks=args.install_hooks, hook_type=args.hook_type, skip_on_missing_conf=args.allow_missing_config, ) elif args.command == 'install-hooks': return install_hooks(runner) elif args.command == 'uninstall': return uninstall(runner, hook_type=args.hook_type) elif args.command == 'clean': return clean(runner) elif args.command == 'autoupdate': if args.tags_only: logger.warning('--tags-only is the default') return autoupdate(runner, tags_only=not args.bleeding_edge) elif args.command == 'migrate-config': return migrate_config(runner) elif args.command == 'run': return run(runner, args) elif args.command == 'sample-config': return sample_config() elif args.command == 'try-repo': return try_repo(args) else: raise NotImplementedError( 'Command {} not implemented.'.format(args.command), ) raise AssertionError( 'Command {} failed to exit with a returncode'.format( args.command), )
def main(argv=None): argv = argv if argv is not None else sys.argv[1:] argv = [five.to_text(arg) for arg in argv] parser = argparse.ArgumentParser() # http://stackoverflow.com/a/8521644/812183 parser.add_argument( '-V', '--version', action='version', version='%(prog)s {}'.format( pkg_resources.get_distribution('pre-commit').version ) ) subparsers = parser.add_subparsers(dest='command') install_parser = subparsers.add_parser( 'install', help='Install the pre-commit script.', ) install_parser.add_argument( '-f', '--overwrite', action='store_true', help='Overwrite existing hooks / remove migration mode.', ) install_parser.add_argument( '--install-hooks', action='store_true', help=( 'Whether to install hook environments for all environments ' 'in the config file.' ), ) install_parser.add_argument( '-t', '--hook-type', choices=('pre-commit', 'pre-push'), default='pre-commit', ) uninstall_parser = subparsers.add_parser( 'uninstall', help='Uninstall the pre-commit script.', ) uninstall_parser.add_argument( '-t', '--hook-type', choices=('pre-commit', 'pre-push'), default='pre-commit', ) subparsers.add_parser('clean', help='Clean out pre-commit files.') subparsers.add_parser( 'autoupdate', help="Auto-update pre-commit config to the latest repos' versions.", ) run_parser = subparsers.add_parser('run', help='Run hooks.') run_parser.add_argument('hook', nargs='?', help='A single hook-id to run') run_parser.add_argument( '--color', default='auto', type=color.use_color, metavar='{' + ','.join(color.COLOR_CHOICES) + '}', help='Whether to use color in output. Defaults to `%(default)s`.', ) run_parser.add_argument( '--no-stash', default=False, action='store_true', help='Use this option to prevent auto stashing of unstaged files.', ) run_parser.add_argument( '--verbose', '-v', action='store_true', default=False, ) run_parser.add_argument( '--origin', '-o', help="The origin branch's commit_id when using `git push`.", ) run_parser.add_argument( '--source', '-s', help="The remote branch's commit_id when using `git push`.", ) run_parser.add_argument( '--allow-unstaged-config', default=False, action='store_true', help=( 'Allow an unstaged config to be present. Note that this will ' 'be stashed before parsing unless --no-stash is specified.' ), ) run_parser.add_argument( '--hook-stage', choices=('commit', 'push'), default='commit', help='The stage during which the hook is fired e.g. commit or push.', ) run_mutex_group = run_parser.add_mutually_exclusive_group(required=False) run_mutex_group.add_argument( '--all-files', '-a', action='store_true', default=False, help='Run on all the files in the repo. Implies --no-stash.', ) run_mutex_group.add_argument( '--files', nargs='*', default=[], help='Specific filenames to run hooks on.', ) help = subparsers.add_parser( 'help', help='Show help for a specific command.', ) help.add_argument('help_cmd', nargs='?', help='Command to show help for.') # Argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ['run'] args = parser.parse_args(argv) if args.command == 'run': args.files = [ os.path.relpath(os.path.abspath(filename), git.get_root()) for filename in args.files ] if args.command == 'help': if args.help_cmd: parser.parse_args([args.help_cmd, '--help']) else: parser.parse_args(['--help']) with error_handler(): runner = Runner.create() if args.command == 'install': return install( runner, overwrite=args.overwrite, hooks=args.install_hooks, hook_type=args.hook_type, ) elif args.command == 'uninstall': return uninstall(runner, hook_type=args.hook_type) elif args.command == 'clean': return clean(runner) elif args.command == 'autoupdate': return autoupdate(runner) elif args.command == 'run': return run(runner, args) else: raise NotImplementedError( 'Command {} not implemented.'.format(args.command) ) raise AssertionError( 'Command {} failed to exit with a returncode'.format(args.command) )
def main(argv=None): argv = argv if argv is not None else sys.argv[1:] argv = [five.to_text(arg) for arg in argv] parser = argparse.ArgumentParser() # https://stackoverflow.com/a/8521644/812183 parser.add_argument( '-V', '--version', action='version', version='%(prog)s {}'.format(C.VERSION), ) subparsers = parser.add_subparsers(dest='command') autoupdate_parser = subparsers.add_parser( 'autoupdate', help="Auto-update pre-commit config to the latest repos' versions.", ) _add_color_option(autoupdate_parser) _add_config_option(autoupdate_parser) autoupdate_parser.add_argument( '--tags-only', action='store_true', help='LEGACY: for compatibility', ) autoupdate_parser.add_argument( '--bleeding-edge', action='store_true', help=('Update to the bleeding edge of `master` instead of the latest ' 'tagged version (the default behavior).'), ) autoupdate_parser.add_argument( '--repo', dest='repos', action='append', metavar='REPO', help='Only update this repository -- may be specified multiple times.', ) clean_parser = subparsers.add_parser( 'clean', help='Clean out pre-commit files.', ) _add_color_option(clean_parser) _add_config_option(clean_parser) gc_parser = subparsers.add_parser('gc', help='Clean unused cached repos.') _add_color_option(gc_parser) _add_config_option(gc_parser) init_templatedir_parser = subparsers.add_parser( 'init-templatedir', help=('Install hook script in a directory intended for use with ' '`git config init.templateDir`.'), ) _add_color_option(init_templatedir_parser) _add_config_option(init_templatedir_parser) init_templatedir_parser.add_argument( 'directory', help='The directory in which to write the hook script.', ) _add_hook_type_option(init_templatedir_parser) install_parser = subparsers.add_parser( 'install', help='Install the pre-commit script.', ) _add_color_option(install_parser) _add_config_option(install_parser) install_parser.add_argument( '-f', '--overwrite', action='store_true', help='Overwrite existing hooks / remove migration mode.', ) install_parser.add_argument( '--install-hooks', action='store_true', help=('Whether to install hook environments for all environments ' 'in the config file.'), ) _add_hook_type_option(install_parser) install_parser.add_argument( '--allow-missing-config', action='store_true', default=False, help=('Whether to allow a missing `pre-commit` configuration file ' 'or exit with a failure code.'), ) install_hooks_parser = subparsers.add_parser( 'install-hooks', help=('Install hook environments for all environments in the config ' 'file. You may find `pre-commit install --install-hooks` more ' 'useful.'), ) _add_color_option(install_hooks_parser) _add_config_option(install_hooks_parser) migrate_config_parser = subparsers.add_parser( 'migrate-config', help='Migrate list configuration to new map configuration.', ) _add_color_option(migrate_config_parser) _add_config_option(migrate_config_parser) run_parser = subparsers.add_parser('run', help='Run hooks.') _add_color_option(run_parser) _add_config_option(run_parser) _add_run_options(run_parser) sample_config_parser = subparsers.add_parser( 'sample-config', help='Produce a sample {} file'.format(C.CONFIG_FILE), ) _add_color_option(sample_config_parser) _add_config_option(sample_config_parser) try_repo_parser = subparsers.add_parser( 'try-repo', help='Try the hooks in a repository, useful for developing new hooks.', ) _add_color_option(try_repo_parser) _add_config_option(try_repo_parser) try_repo_parser.add_argument( 'repo', help='Repository to source hooks from.', ) try_repo_parser.add_argument( '--ref', '--rev', help=('Manually select a rev to run against, otherwise the `HEAD` ' 'revision will be used.'), ) _add_run_options(try_repo_parser) uninstall_parser = subparsers.add_parser( 'uninstall', help='Uninstall the pre-commit script.', ) _add_color_option(uninstall_parser) _add_config_option(uninstall_parser) _add_hook_type_option(uninstall_parser) help = subparsers.add_parser( 'help', help='Show help for a specific command.', ) help.add_argument('help_cmd', nargs='?', help='Command to show help for.') # argparse doesn't really provide a way to use a `default` subparser if len(argv) == 0: argv = ['run'] args = parser.parse_args(argv) if args.command == 'help' and args.help_cmd: parser.parse_args([args.help_cmd, '--help']) elif args.command == 'help': parser.parse_args(['--help']) with error_handler(), logging_handler(args.color): if args.command not in COMMANDS_NO_GIT: _adjust_args_and_chdir(args) git.check_for_cygwin_mismatch() store = Store() store.mark_config_used(args.config) if args.command == 'autoupdate': if args.tags_only: logger.warning('--tags-only is the default') return autoupdate( args.config, store, tags_only=not args.bleeding_edge, repos=args.repos, ) elif args.command == 'clean': return clean(store) elif args.command == 'gc': return gc(store) elif args.command == 'install': return install( args.config, store, hook_types=args.hook_types, overwrite=args.overwrite, hooks=args.install_hooks, skip_on_missing_config=args.allow_missing_config, ) elif args.command == 'init-templatedir': return init_templatedir( args.config, store, args.directory, hook_types=args.hook_types, ) elif args.command == 'install-hooks': return install_hooks(args.config, store) elif args.command == 'migrate-config': return migrate_config(args.config) elif args.command == 'run': return run(args.config, store, args) elif args.command == 'sample-config': return sample_config() elif args.command == 'try-repo': return try_repo(args) elif args.command == 'uninstall': return uninstall(hook_types=args.hook_types) else: raise NotImplementedError( 'Command {} not implemented.'.format(args.command), ) raise AssertionError( 'Command {} failed to exit with a returncode'.format( args.command), )
def test_install_hooks_dead_symlink(in_git_dir, store): hook = in_git_dir.join('.git/hooks').ensure_dir().join('pre-commit') os.symlink('/fake/baz', hook.strpath) install(C.CONFIG_FILE, store, hook_types=['pre-commit']) assert hook.exists()
def test_install_refuses_core_hookspath(in_git_dir, store): cmd_output('git', 'config', '--local', 'core.hooksPath', 'hooks') assert install(C.CONFIG_FILE, store, hook_types=['pre-commit'])
def test_install_hooks_directory_not_present(in_git_dir, store): # Simulate some git clients which don't make .git/hooks #234 if in_git_dir.join('.git/hooks').exists(): # pragma: no cover (odd git) in_git_dir.join('.git/hooks').remove() install(C.CONFIG_FILE, store, hook_types=['pre-commit']) assert in_git_dir.join('.git/hooks/pre-commit').exists()
def test_install_pre_commit(in_git_dir, store): assert not install(C.CONFIG_FILE, store, hook_types=['pre-commit']) assert os.access(in_git_dir.join('.git/hooks/pre-commit').strpath, os.X_OK) assert not install(C.CONFIG_FILE, store, hook_types=['pre-push']) assert os.access(in_git_dir.join('.git/hooks/pre-push').strpath, os.X_OK)