def test_pre_push_legacy(tempdir_factory):
    upstream = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    path = tempdir_factory.get()
    cmd_output('git', 'clone', upstream, path)
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        hook_path = runner.get_hook_path('pre-push')
        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'
                'read lr ls rr rs\n'
                'test -n "$lr" -a -n "$ls" -a -n "$rr" -a -n "$rs"\n'
                'echo legacy\n',
            )
        make_executable(hook_path)

        install(runner, 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 write_executable(shebang, filename='run'):
    os.mkdir('bin')
    path = os.path.join('bin', filename)
    with io.open(path, 'w') as f:
        f.write('#!{}'.format(shebang))
    make_executable(path)
    return path
Exemple #3
0
def test_run_legacy_executes_legacy_script(tmpdir, capfd):
    hook = tmpdir.join('pre-commit.legacy')
    hook.write('#!/usr/bin/env bash\necho hi "$@"\nexit 1\n')
    make_executable(hook)
    retv, stdin = hook_impl._run_legacy('pre-commit', tmpdir, ('arg1', 'arg2'))
    assert capfd.readouterr().out.strip() == 'hi arg1 arg2'
    assert (retv, stdin) == (1, b'')
def test_unhealthy_then_replaced(python_dir):
    prefix, tmpdir = python_dir

    python.install_environment(prefix, C.DEFAULT, ())

    # simulate an exe which returns an old version
    exe_name = win_exe('python')
    py_exe = prefix.path(python.bin_dir('py_env-default'), exe_name)
    os.rename(py_exe, f'{py_exe}.tmp')

    with open(py_exe, 'w') as f:
        f.write('#!/usr/bin/env bash\necho 1.2.3\n')
    make_executable(py_exe)

    # should be unhealthy due to version mismatch
    ret = python.health_check(prefix, C.DEFAULT)
    assert ret == (
        f'virtualenv python version did not match created version:\n'
        f'- actual version: 1.2.3\n'
        f'- expected version: {python._version_info(sys.executable)}\n')

    # now put the exe back and it should be healthy again
    os.replace(f'{py_exe}.tmp', py_exe)

    assert python.health_check(prefix, C.DEFAULT) is None
Exemple #5
0
def prepare_commit_msg_repo(tempdir_factory):
    path = git_dir(tempdir_factory)
    script_name = 'add_sign_off.sh'
    config = {
        'repo': 'local',
        'hooks': [{
            'id': 'add-signoff',
            'name': 'Add "Signed off by:"',
            'entry': './{}'.format(script_name),
            'language': 'script',
            'stages': ['prepare-commit-msg'],
        }],
    }
    write_config(path, config)
    with cwd(path):
        with io.open(script_name, 'w') as script_file:
            script_file.write(
                '#!/usr/bin/env bash\n'
                'set -eu\n'
                'echo "\nSigned off by: " >> "$1"\n',
            )
            make_executable(script_name)
        cmd_output('git', 'add', '.')
        git_commit(msg=prepare_commit_msg_repo.__name__)
        yield 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 write_executable(shebang, filename='run'):
    os.mkdir('bin')
    path = os.path.join('bin', filename)
    with io.open(path, 'w') as f:
        f.write('#!{}'.format(shebang))
    make_executable(path)
    return path
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')
Exemple #9
0
def install(
    config_file,
    store,
    overwrite=False,
    hooks=False,
    hook_type='pre-commit',
    skip_on_missing_conf=False,
):
    """Install the pre-commit hooks."""
    if cmd_output('git', 'config', 'core.hooksPath', retcode=None)[1].strip():
        logger.error(
            'Cowardly refusing to install hooks with `core.hooksPath` set.\n'
            'hint: `git config --unset-all core.hooksPath`', )
        return 1

    hook_path, legacy_path = _hook_paths(hook_type)

    mkdirp(os.path.dirname(hook_path))

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        os.rename(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            'Running in migration mode with existing hooks at {}\n'
            'Use -f to use only pre-commit.'.format(legacy_path), )

    params = {
        'CONFIG': config_file,
        'HOOK_TYPE': hook_type,
        'INSTALL_PYTHON': sys.executable,
        'SKIP_ON_MISSING_CONFIG': skip_on_missing_conf,
    }

    with io.open(hook_path, 'w') as hook_file:
        contents = resource_text('hook-tmpl')
        before, rest = contents.split(TEMPLATE_START)
        to_template, after = rest.split(TEMPLATE_END)

        before = before.replace('#!/usr/bin/env python3', shebang())

        hook_file.write(before + TEMPLATE_START)
        for line in to_template.splitlines():
            var = line.split()[0]
            hook_file.write('{} = {!r}\n'.format(var, params[var]))
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line('pre-commit installed at {}'.format(hook_path))

    # If they requested we install all of the hooks, do so.
    if hooks:
        install_hooks(config_file, store)

    return 0
def test_shebang_posix_on_path(tmpdir):
    exe = tmpdir.join(f'python{sys.version_info[0]}').ensure()
    make_executable(exe)

    with mock.patch.object(sys, 'platform', 'posix'):
        with mock.patch.object(os, 'defpath', tmpdir.strpath):
            expected = f'#!/usr/bin/env python{sys.version_info[0]}'
            assert shebang() == expected
Exemple #11
0
def test_uninstall_doesnt_remove_not_our_hooks(in_git_dir):
    pre_commit = in_git_dir.join('.git/hooks').ensure_dir().join('pre-commit')
    pre_commit.write('#!/usr/bin/env bash\necho 1\n')
    make_executable(pre_commit.strpath)

    assert uninstall(hook_types=['pre-commit']) == 0

    assert pre_commit.exists()
def test_uninstall_doesnt_remove_not_our_hooks(in_git_dir):
    pre_commit = in_git_dir.join('.git/hooks').ensure_dir().join('pre-commit')
    pre_commit.write('#!/usr/bin/env bash\necho 1\n')
    make_executable(pre_commit.strpath)

    assert uninstall() == 0

    assert pre_commit.exists()
def install(
        config_file, store,
        overwrite=False, hooks=False, hook_type='pre-commit',
        skip_on_missing_conf=False,
):
    """Install the pre-commit hooks."""
    if cmd_output('git', 'config', 'core.hooksPath', retcode=None)[1].strip():
        logger.error(
            'Cowardly refusing to install hooks with `core.hooksPath` set.\n'
            'hint: `git config --unset-all core.hooksPath`',
        )
        return 1

    hook_path, legacy_path = _hook_paths(hook_type)

    mkdirp(os.path.dirname(hook_path))

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        shutil.move(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            'Running in migration mode with existing hooks at {}\n'
            'Use -f to use only pre-commit.'.format(legacy_path),
        )

    params = {
        'CONFIG': config_file,
        'HOOK_TYPE': hook_type,
        'INSTALL_PYTHON': sys.executable,
        'SKIP_ON_MISSING_CONFIG': skip_on_missing_conf,
    }

    with io.open(hook_path, 'w') as hook_file:
        contents = resource_text('hook-tmpl')
        before, rest = contents.split(TEMPLATE_START)
        to_template, after = rest.split(TEMPLATE_END)

        before = before.replace('#!/usr/bin/env python3', shebang())

        hook_file.write(before + TEMPLATE_START)
        for line in to_template.splitlines():
            var = line.split()[0]
            hook_file.write('{} = {!r}\n'.format(var, params[var]))
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line('pre-commit installed at {}'.format(hook_path))

    # If they requested we install all of the hooks, do so.
    if hooks:
        install_hooks(config_file, store)

    return 0
Exemple #14
0
def test_cmd_output_no_shebang(tmpdir, fn):
    f = tmpdir.join('f').ensure()
    make_executable(f)

    # previously this raised `OSError` -- the output is platform specific
    ret, out, _ = fn(str(f), retcode=None, stderr=subprocess.STDOUT)
    assert ret == 1
    assert isinstance(out, bytes)
    assert out.endswith(b'\n')
def install(
        runner, overwrite=False, hooks=False, hook_type='pre-commit',
        skip_on_missing_conf=False,
):
    """Install the pre-commit hooks."""
    hook_path = runner.get_hook_path(hook_type)
    legacy_path = hook_path + '.legacy'

    mkdirp(os.path.dirname(hook_path))

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        os.rename(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            'Running in migration mode with existing hooks at {}\n'
            'Use -f to use only pre-commit.'.format(
                legacy_path,
            ),
        )

    with io.open(hook_path, 'w') as pre_commit_file_obj:
        if hook_type == 'pre-push':
            with io.open(resource_filename('pre-push-tmpl')) as f:
                hook_specific_contents = f.read()
        elif hook_type == 'commit-msg':
            with io.open(resource_filename('commit-msg-tmpl')) as f:
                hook_specific_contents = f.read()
        elif hook_type == 'pre-commit':
            hook_specific_contents = ''
        else:
            raise AssertionError('Unknown hook type: {}'.format(hook_type))

        skip_on_missing_conf = 'true' if skip_on_missing_conf else 'false'
        contents = io.open(resource_filename('hook-tmpl')).read().format(
            sys_executable=pipes.quote(sys.executable),
            hook_type=hook_type,
            hook_specific=hook_specific_contents,
            config_file=runner.config_file,
            skip_on_missing_conf=skip_on_missing_conf,
        )
        pre_commit_file_obj.write(contents)
    make_executable(hook_path)

    output.write_line('pre-commit installed at {}'.format(hook_path))

    # If they requested we install all of the hooks, do so.
    if hooks:
        install_hooks(runner)

    return 0
def install(
    runner,
    overwrite=False,
    hooks=False,
    hook_type='pre-commit',
    skip_on_missing_conf=False,
):
    """Install the pre-commit hooks."""
    hook_path = runner.get_hook_path(hook_type)
    legacy_path = hook_path + '.legacy'

    mkdirp(os.path.dirname(hook_path))

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        os.rename(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            'Running in migration mode with existing hooks at {}\n'
            'Use -f to use only pre-commit.'.format(legacy_path, ), )

    with io.open(hook_path, 'w') as pre_commit_file_obj:
        if hook_type == 'pre-push':
            with io.open(resource_filename('pre-push-tmpl')) as f:
                hook_specific_contents = f.read()
        elif hook_type == 'commit-msg':
            with io.open(resource_filename('commit-msg-tmpl')) as f:
                hook_specific_contents = f.read()
        elif hook_type == 'pre-commit':
            hook_specific_contents = ''
        else:
            raise AssertionError('Unknown hook type: {}'.format(hook_type))

        skip_on_missing_conf = 'true' if skip_on_missing_conf else 'false'
        contents = io.open(resource_filename('hook-tmpl')).read().format(
            sys_executable=sys.executable,
            hook_type=hook_type,
            hook_specific=hook_specific_contents,
            skip_on_missing_conf=skip_on_missing_conf,
        )
        pre_commit_file_obj.write(contents)
    make_executable(hook_path)

    output.write_line('pre-commit installed at {}'.format(hook_path))

    # If they requested we install all of the hooks, do so.
    if hooks:
        install_hooks(runner)

    return 0
Exemple #17
0
def test_run_legacy_recursive(tmpdir):
    hook = tmpdir.join('pre-commit.legacy').ensure()
    make_executable(hook)

    # simulate a call being recursive
    def call(*_, **__):
        return hook_impl._run_legacy('pre-commit', tmpdir, ())

    with mock.patch.object(subprocess, 'run', call):
        with pytest.raises(SystemExit):
            call()
Exemple #18
0
def test_exclude_types_hook_repository(cap_out, store, tempdir_factory):
    git_path = make_consuming_repo(tempdir_factory, 'exclude_types_repo')
    with cwd(git_path):
        with io.open('exe', 'w') as exe:
            exe.write('#!/usr/bin/env python3\n')
        make_executable('exe')
        cmd_output('git', 'add', 'exe')
        stage_a_file('bar.py')
        ret, printed = _do_run(cap_out, store, git_path, run_opts())
        assert ret == 1
        assert b'bar.py' in printed
        assert b'exe' not in printed
Exemple #19
0
def test_uninstall_doesnt_remove_not_our_hooks(tempdir_factory):
    path = git_dir(tempdir_factory)
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)
        mkdirp(os.path.join(path, '.git/hooks'))
        with io.open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
            f.write('#!/usr/bin/env bash\necho 1\n')
        make_executable(f.name)

        assert uninstall(runner) == 0

        assert os.path.exists(os.path.join(path, '.git/hooks/pre-commit'))
def test_uninstall_doesnt_remove_not_our_hooks(tempdir_factory):
    path = git_dir(tempdir_factory)
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)
        mkdirp(os.path.dirname(runner.pre_commit_path))
        with io.open(runner.pre_commit_path, 'w') as pre_commit_file:
            pre_commit_file.write('#!/usr/bin/env bash\necho 1\n')
        make_executable(runner.pre_commit_path)

        assert uninstall(runner) == 0

        assert os.path.exists(runner.pre_commit_path)
Exemple #21
0
def test_uninstall_doesnt_remove_not_our_hooks(tempdir_factory):
    path = git_dir(tempdir_factory)
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)
        mkdirp(os.path.dirname(runner.pre_commit_path))
        with io.open(runner.pre_commit_path, 'w') as pre_commit_file:
            pre_commit_file.write('#!/usr/bin/env bash\necho 1\n')
        make_executable(runner.pre_commit_path)

        assert uninstall(runner) == 0

        assert os.path.exists(runner.pre_commit_path)
Exemple #22
0
def test_exclude_types_hook_repository(cap_out, store, tempdir_factory):
    git_path = make_consuming_repo(tempdir_factory, 'exclude_types_repo')
    with cwd(git_path):
        with open('exe', 'w') as exe:
            exe.write('#!/usr/bin/env python3\n')
        make_executable('exe')
        cmd_output('git', 'add', 'exe')
        stage_a_file('bar.py')
        ret, printed = _do_run(cap_out, store, git_path, run_opts())
        assert ret == 1
        assert b'bar.py' in printed
        assert b'exe' not in printed
def install(
        runner, overwrite=False, hooks=False, hook_type='pre-commit',
        skip_on_missing_conf=False,
):
    """Install the pre-commit hooks."""
    hook_path = runner.get_hook_path(hook_type)
    legacy_path = hook_path + '.legacy'

    mkdirp(os.path.dirname(hook_path))

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        os.rename(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            'Running in migration mode with existing hooks at {}\n'
            'Use -f to use only pre-commit.'.format(legacy_path),
        )

    params = {
        'CONFIG': runner.config_file,
        'HOOK_TYPE': hook_type,
        'INSTALL_PYTHON': sys.executable,
        'SKIP_ON_MISSING_CONFIG': skip_on_missing_conf,
    }

    with io.open(hook_path, 'w') as hook_file:
        with io.open(resource_filename('hook-tmpl')) as f:
            contents = f.read()
        before, rest = contents.split(TEMPLATE_START)
        to_template, after = rest.split(TEMPLATE_END)

        hook_file.write(before + TEMPLATE_START)
        for line in to_template.splitlines():
            var = line.split()[0]
            hook_file.write('{} = {!r}\n'.format(var, params[var]))
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line('pre-commit installed at {}'.format(hook_path))

    # If they requested we install all of the hooks, do so.
    if hooks:
        install_hooks(runner)

    return 0
Exemple #24
0
def _install_hook_script(
        config_file: str,
        hook_type: str,
        overwrite: bool = False,
        skip_on_missing_config: bool = False,
        git_dir: Optional[str] = None,
) -> None:
    hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)

    os.makedirs(os.path.dirname(hook_path), exist_ok=True)

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        shutil.move(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            f'Running in migration mode with existing hooks at {legacy_path}\n'
            f'Use -f to use only pre-commit.',
        )

    args = ['hook-impl', f'--config={config_file}', f'--hook-type={hook_type}']
    if skip_on_missing_config:
        args.append('--skip-on-missing-config')

    with open(hook_path, 'w') as hook_file:
        contents = resource_text('hook-tmpl')
        before, rest = contents.split(TEMPLATE_START)
        _, after = rest.split(TEMPLATE_END)

        # on windows always use `/bin/sh` since `bash` might not be on PATH
        # though we use bash-specific features `sh` on windows is actually
        # bash in "POSIXLY_CORRECT" mode which still supports the features we
        # use: subshells / arrays
        if sys.platform == 'win32':  # pragma: win32 cover
            hook_file.write('#!/bin/sh\n')

        hook_file.write(before + TEMPLATE_START)
        hook_file.write(f'INSTALL_PYTHON={shlex.quote(sys.executable)}\n')
        # TODO: python3.8+: shlex.join
        args_s = ' '.join(shlex.quote(part) for part in args)
        hook_file.write(f'ARGS=({args_s})\n')
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line(f'pre-commit installed at {hook_path}')
Exemple #25
0
def test_failing_existing_hook_returns_1(tempdir_factory, store):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        # Write out a failing "old" hook
        os.makedirs(os.path.join(path, '.git/hooks'), exist_ok=True)
        with open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
            f.write('#!/usr/bin/env bash\necho "fail!"\nexit 1\n')
        make_executable(f.name)

        assert install(C.CONFIG_FILE, store, hook_types=['pre-commit']) == 0

        # We should get a failure from the legacy hook
        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 1
        assert FAIL_OLD_HOOK.match(output)
def test_failing_existing_hook_returns_1(tempdir_factory, store):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        # Write out a failing "old" hook
        mkdirp(os.path.join(path, '.git/hooks'))
        with io.open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
            f.write('#!/usr/bin/env bash\necho "fail!"\nexit 1\n')
        make_executable(f.name)

        assert install(C.CONFIG_FILE, store) == 0

        # We should get a failure from the legacy hook
        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 1
        assert FAIL_OLD_HOOK.match(output)
Exemple #27
0
def test_install_overwrite(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # Write out the "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)

        assert install(runner, overwrite=True) == 0

        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 0
        assert NORMAL_PRE_COMMIT_RUN.match(output)
def test_install_overwrite(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # Write out the "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)

        assert install(runner, overwrite=True) == 0

        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 0
        assert NORMAL_PRE_COMMIT_RUN.match(output)
def test_failing_existing_hook_returns_1(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # Write out a failing "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 "fail!"\nexit 1\n')
        make_executable(runner.pre_commit_path)

        assert install(runner) == 0

        # We should get a failure from the legacy hook
        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 1
        assert FAIL_OLD_HOOK.match(output)
Exemple #30
0
def test_failing_existing_hook_returns_1(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # Write out a failing "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 "fail!"\nexit 1\n')
        make_executable(runner.pre_commit_path)

        assert install(runner) == 0

        # We should get a failure from the legacy hook
        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 1
        assert FAIL_OLD_HOOK.match(output)
def install(runner, overwrite=False, hooks=False, hook_type='pre-commit'):
    """Install the pre-commit hooks."""
    hook_path = runner.get_hook_path(hook_type)
    legacy_path = hook_path + '.legacy'

    mkdirp(os.path.dirname(hook_path))

    # If we have an existing hook, move it to pre-commit.legacy
    if (os.path.lexists(hook_path) and not is_our_pre_commit(hook_path)
            and not is_previous_pre_commit(hook_path)):
        os.rename(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        print('Running in migration mode with existing hooks at {0}\n'
              'Use -f to use only pre-commit.'.format(legacy_path, ))

    with io.open(hook_path, 'w') as pre_commit_file_obj:
        if hook_type == 'pre-push':
            with io.open(resource_filename('pre-push-tmpl')) as fp:
                pre_push_contents = fp.read()
        else:
            pre_push_contents = ''

        contents = io.open(resource_filename('hook-tmpl')).read().format(
            sys_executable=sys.executable,
            hook_type=hook_type,
            pre_push=pre_push_contents,
        )
        pre_commit_file_obj.write(contents)
    make_executable(hook_path)

    print('pre-commit installed at {0}'.format(hook_path))

    # If they requested we install all of the hooks, do so.
    if hooks:
        # Set up our logging handler
        logger.addHandler(LoggingHandler(False))
        logger.setLevel(logging.INFO)
        for repository in runner.repositories:
            repository.require_installed()

    return 0
def _install_hook_script(
    config_file: str,
    hook_type: str,
    overwrite: bool = False,
    skip_on_missing_config: bool = False,
    git_dir: Optional[str] = None,
) -> None:
    hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)

    os.makedirs(os.path.dirname(hook_path), exist_ok=True)

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        shutil.move(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            f'Running in migration mode with existing hooks at {legacy_path}\n'
            f'Use -f to use only pre-commit.', )

    params = {
        'CONFIG': config_file,
        'HOOK_TYPE': hook_type,
        'INSTALL_PYTHON': sys.executable,
        'SKIP_ON_MISSING_CONFIG': skip_on_missing_config,
    }

    with open(hook_path, 'w') as hook_file:
        contents = resource_text('hook-tmpl')
        before, rest = contents.split(TEMPLATE_START)
        to_template, after = rest.split(TEMPLATE_END)

        before = before.replace('#!/usr/bin/env python3', shebang())

        hook_file.write(before + TEMPLATE_START)
        for line in to_template.splitlines():
            var = line.split()[0]
            hook_file.write(f'{var} = {params[var]!r}\n')
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line(f'pre-commit installed at {hook_path}')
Exemple #33
0
def test_uninstall_restores_legacy_hooks(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # 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)

        # Now install and uninstall pre-commit
        assert install(runner) == 0
        assert uninstall(runner) == 0

        # 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)
def _install_hook_script(
    config_file: str,
    hook_type: str,
    overwrite: bool = False,
    skip_on_missing_config: bool = False,
    git_dir: Optional[str] = None,
) -> None:
    hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)

    os.makedirs(os.path.dirname(hook_path), exist_ok=True)

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        shutil.move(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            f"Running in migration mode with existing hooks at {legacy_path}\n"
            f"Use -f to use only pre-commit.", )

    args = ["hook-impl", f"--config={config_file}", f"--hook-type={hook_type}"]
    if skip_on_missing_config:
        args.append("--skip-on-missing-config")
    params = {"INSTALL_PYTHON": sys.executable, "ARGS": args}

    with open(hook_path, "w") as hook_file:
        contents = resource_text("hook-tmpl")
        before, rest = contents.split(TEMPLATE_START)
        to_template, after = rest.split(TEMPLATE_END)

        before = before.replace("#!/usr/bin/env python3", shebang())

        hook_file.write(before + TEMPLATE_START)
        for line in to_template.splitlines():
            var = line.split()[0]
            hook_file.write(f"{var} = {params[var]!r}\n")
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line(f"pre-commit installed at {hook_path}")
def test_uninstall_restores_legacy_hooks(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # 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)

        # Now install and uninstall pre-commit
        assert install(runner) == 0
        assert uninstall(runner) == 0

        # 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)
Exemple #36
0
def test_commit_msg_legacy(commit_msg_repo, tempdir_factory, store):
    hook_path = os.path.join(commit_msg_repo, '.git/hooks/commit-msg')
    os.makedirs(os.path.dirname(hook_path), exist_ok=True)
    with 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_types=['commit-msg'])

    msg = 'Hi\nSigned off by: asottile'
    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('Must have "Signed off by:"...')
def _install_hook_script(
        config_file: str,
        hook_type: str,
        overwrite: bool = False,
        skip_on_missing_config: bool = False,
        git_dir: Optional[str] = None,
) -> None:
    hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir)

    os.makedirs(os.path.dirname(hook_path), exist_ok=True)

    # If we have an existing hook, move it to pre-commit.legacy
    if os.path.lexists(hook_path) and not is_our_script(hook_path):
        shutil.move(hook_path, legacy_path)

    # If we specify overwrite, we simply delete the legacy file
    if overwrite and os.path.exists(legacy_path):
        os.remove(legacy_path)
    elif os.path.exists(legacy_path):
        output.write_line(
            f'Running in migration mode with existing hooks at {legacy_path}\n'
            f'Use -f to use only pre-commit.',
        )

    args = ['hook-impl', f'--config={config_file}', f'--hook-type={hook_type}']
    if skip_on_missing_config:
        args.append('--skip-on-missing-config')

    with open(hook_path, 'w') as hook_file:
        contents = resource_text('hook-tmpl')
        before, rest = contents.split(TEMPLATE_START)
        _, after = rest.split(TEMPLATE_END)

        hook_file.write(before + TEMPLATE_START)
        hook_file.write(f'INSTALL_PYTHON={shlex.quote(sys.executable)}\n')
        # TODO: python3.8+: shlex.join
        args_s = ' '.join(shlex.quote(part) for part in args)
        hook_file.write(f'ARGS=({args_s})\n')
        hook_file.write(TEMPLATE_END + after)
    make_executable(hook_path)

    output.write_line(f'pre-commit installed at {hook_path}')
def test_replace_old_commit_script(tempdir_factory, store):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        # Install a script that looks like our old script
        pre_commit_contents = resource_text('hook-tmpl')
        new_contents = pre_commit_contents.replace(
            CURRENT_HASH.decode(), PRIOR_HASHES[-1].decode(),
        )

        os.makedirs(os.path.join(path, '.git/hooks'), exist_ok=True)
        with open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
            f.write(new_contents)
        make_executable(f.name)

        # Install normally
        assert install(C.CONFIG_FILE, store, hook_types=['pre-commit']) == 0

        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 0
        NORMAL_PRE_COMMIT_RUN.assert_matches(output)
Exemple #39
0
def test_install_existing_hook_no_overwrite_idempotent(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # 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)

        # Install twice
        assert install(runner) == 0
        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_commit_msg_legacy(commit_msg_repo, tempdir_factory, store):
    hook_path = os.path.join(commit_msg_repo, '.git/hooks/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='commit-msg')

    msg = 'Hi\nSigned off by: asottile'
    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('Must have "Signed off by:"...')
def test_install_existing_hook_no_overwrite_idempotent(tempdir_factory):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        runner = Runner(path, C.CONFIG_FILE)

        # 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)

        # Install twice
        assert install(runner) == 0
        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_replace_old_commit_script(tempdir_factory, store):
    path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
    with cwd(path):
        # Install a script that looks like our old script
        pre_commit_contents = resource_text('hook-tmpl')
        new_contents = pre_commit_contents.replace(
            CURRENT_HASH, PRIOR_HASHES[-1],
        )

        mkdirp(os.path.join(path, '.git/hooks'))
        with io.open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
            f.write(new_contents)
        make_executable(f.name)

        # Install normally
        assert install(C.CONFIG_FILE, store) == 0

        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 0
        assert NORMAL_PRE_COMMIT_RUN.match(output)
Exemple #43
0
def test_unhealthy_then_replaced(python_dir):
    prefix, tmpdir = python_dir

    python.install_environment(prefix, C.DEFAULT, ())

    # simulate an exe which returns an old version
    exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
    py_exe = prefix.path(python.bin_dir('py_env-default'), exe_name)
    os.rename(py_exe, f'{py_exe}.tmp')

    with open(py_exe, 'w') as f:
        f.write('#!/usr/bin/env bash\necho 1.2.3\n')
    make_executable(py_exe)

    # should be unhealthy due to version mismatch
    assert python.healthy(prefix, C.DEFAULT) is False

    # now put the exe back and it should be healthy again
    os.replace(f'{py_exe}.tmp', py_exe)

    assert python.healthy(prefix, C.DEFAULT) is True
Exemple #44
0
def test_commit_msg_legacy(commit_msg_repo, tempdir_factory):
    runner = Runner(commit_msg_repo, C.CONFIG_FILE)

    hook_path = runner.get_hook_path('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(runner, hook_type='commit-msg')

    msg = 'Hi\nSigned off by: asottile'
    retc, out = _get_commit_output(tempdir_factory, commit_msg=msg)
    assert retc == 0
    first_line, second_line = out.splitlines()[:2]
    assert first_line == 'legacy'
    assert second_line.startswith('Must have "Signed off by:"...')
def test_replace_old_commit_script(tempdir_factory, store):
    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.join(path, '.git/hooks'))
        with io.open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
            f.write(new_contents)
        make_executable(f.name)

        # Install normally
        assert install(runner, store) == 0

        ret, output = _get_commit_output(tempdir_factory)
        assert ret == 0
        assert NORMAL_PRE_COMMIT_RUN.match(output)
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(
            IDENTIFYING_HASH,
            PREVIOUS_IDENTIFYING_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)
Exemple #47
0
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_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 _write_legacy_hook(path):
    mkdirp(os.path.join(path, '.git/hooks'))
    with io.open(os.path.join(path, '.git/hooks/pre-commit'), 'w') as f:
        f.write('#!/usr/bin/env bash\necho "legacy hook"\n')
    make_executable(f.name)
def test_simple_case(tmpdir):
    x = tmpdir.join('f')
    x.write_text('#!/usr/bin/env python', encoding='UTF-8')
    make_executable(x.strpath)
    assert parse_shebang.parse_filename(x.strpath) == ('python',)