diff --git a/pre_commit/constants.py b/pre_commit/constants.py index a406fc22..8a52a03c 100644 --- a/pre_commit/constants.py +++ b/pre_commit/constants.py @@ -1,7 +1,7 @@ CONFIG_FILE = '.pre-commit-config.yaml' -PRE_COMMIT_DIR = '.pre-commit-files' +HOOKS_WORKSPACE = '.pre-commit-files' MANIFEST_FILE = 'manifest.yaml' diff --git a/pre_commit/git.py b/pre_commit/git.py index d72008d0..7f057b2d 100644 --- a/pre_commit/git.py +++ b/pre_commit/git.py @@ -1,23 +1,18 @@ import os import pkg_resources -import pre_commit.constants as C from plumbum import local +# TODO: optimization: memoize based on local.cwd.getpath() def get_root(): return local['git']['rev-parse', '--show-toplevel']().strip() + def get_pre_commit_path(): return os.path.join(get_root(), '.git/hooks/pre-commit') -def get_pre_commit_dir_path(): - return os.path.join(get_root(), C.PRE_COMMIT_DIR) - -def create_pre_commit_package_dir(): - local.path(get_pre_commit_dir_path()).mkdir() - def create_pre_commit(): path = get_pre_commit_path() pre_commit_file = pkg_resources.resource_filename('pre_commit', 'resources/pre-commit.sh') @@ -28,4 +23,6 @@ def remove_pre_commit(): local.path(get_pre_commit_path()).delete() - +def get_head_sha(git_repo_path): + with local.cwd(git_repo_path): + return (local['git']['rev-parse', 'HEAD'])().strip() \ No newline at end of file diff --git a/pre_commit/hooks_workspace.py b/pre_commit/hooks_workspace.py new file mode 100644 index 00000000..e79ad2b9 --- /dev/null +++ b/pre_commit/hooks_workspace.py @@ -0,0 +1,20 @@ + +import contextlib +import os.path +from plumbum import local + +import pre_commit.constants as C +from pre_commit import git + + +def get_pre_commit_dir_path(): + return os.path.join(git.get_root(), C.HOOKS_WORKSPACE) + +@contextlib.contextmanager +def in_hooks_workspace(): + """Change into the hooks workspace. If it does not exist create it.""" + if not os.path.exists(get_pre_commit_dir_path()): + local.path(get_pre_commit_dir_path()).mkdir() + + with local.cwd(get_pre_commit_dir_path()): + yield \ No newline at end of file diff --git a/pre_commit/repo_installer.py b/pre_commit/repo_installer.py index 322950a0..9ec0673c 100644 --- a/pre_commit/repo_installer.py +++ b/pre_commit/repo_installer.py @@ -1,46 +1,44 @@ -import contextlib +import contextlib from plumbum import local -from pre_commit import git + +from pre_commit.hooks_workspace import in_hooks_workspace class RepoInstaller(object): - def __init__(self, git_repo_path, sha): - self.git_repo_path = git_repo_path - self.sha = sha + def __init__(self, repo_config): + self.repo_config = repo_config + + @property + def repo_url(self): + return self.repo_config['repo'] + + @property + def sha(self): + return self.repo_config['sha'] @contextlib.contextmanager def in_checkout(self): - with local.cwd(git.get_pre_commit_dir_path()): + with in_hooks_workspace(): with local.cwd(self.sha): yield def create(self): - git.create_pre_commit_package_dir() - - with local.cwd(git.get_pre_commit_dir_path()): + with in_hooks_workspace(): if local.path(self.sha).exists(): # Project already exists, no reason to re-create it return - local['git']['clone', self.git_repo_path, self.sha]() + local['git']['clone', self.repo_url, self.sha]() with self.in_checkout(): local['git']['checkout', self.sha]() def install(self): + # Create if we have not already + self.create() # TODO: need to take in the config here and determine if we actually # need to run any installers (and what languages to install) with self.in_checkout(): if local.path('setup.py').exists(): local['virtualenv']['py_env']() local['bash']['-c', 'source py_env/bin/activate && pip install .']() - - -def create_repo_in_env(git_repo_path, sha): - project = RepoInstaller(git_repo_path, sha) - project.create() - -def install_pre_commit(git_repo_path, sha): - project = RepoInstaller(git_repo_path, sha) - project.create() - project.install() \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 377a7bb5..aa92ff11 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,14 @@ +from __future__ import absolute_import +import jsonschema import pytest -import pre_commit.constants as C +import time from plumbum import local +import pre_commit.constants as C +from pre_commit import git +from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA + @pytest.yield_fixture def empty_git_dir(tmpdir): @@ -11,10 +17,9 @@ def empty_git_dir(tmpdir): yield tmpdir.strpath - def add_and_commit(): local['git']['add', '.']() - local['git']['commit', '-m', 'random commit']() + local['git']['commit', '-m', 'random commit {0}'.format(time.time())]() @pytest.yield_fixture @@ -42,8 +47,7 @@ hooks: @pytest.yield_fixture def python_pre_commit_git_repo(dummy_pre_commit_hooks_git_repo): - local.path('setup.py').write( -""" + local.path('setup.py').write(""" from setuptools import find_packages from setuptools import setup @@ -53,7 +57,7 @@ setup( packages=find_packages('.'), entry_points={ 'console_scripts': [ - 'entry = foo.main:func' + 'foo = foo.main:func' ], } ) @@ -66,15 +70,28 @@ setup( with local.cwd(foo_module): local.path('__init__.py').write('') - local.path('main.py').write( -""" - + local.path('main.py').write(""" def func(): return 0 - """ ) add_and_commit() yield dummy_pre_commit_hooks_git_repo + + +@pytest.fixture +def config_for_python_pre_commit_git_repo(python_pre_commit_git_repo): + config = { + 'repo': python_pre_commit_git_repo, + 'sha': git.get_head_sha(python_pre_commit_git_repo), + 'hooks': [{ + 'id': 'foo', + 'files': '*.py', + }], + } + + jsonschema.validate([config], CONFIG_JSON_SCHEMA) + + return config \ No newline at end of file diff --git a/tests/git_test.py b/tests/git_test.py index 9f413294..7dc5d05e 100644 --- a/tests/git_test.py +++ b/tests/git_test.py @@ -16,7 +16,9 @@ def test_get_root(empty_git_dir): def test_get_pre_commit_path(empty_git_dir): - assert git.get_pre_commit_path() == '{0}/.git/hooks/pre-commit'.format(empty_git_dir) + assert git.get_pre_commit_path() == '{0}/.git/hooks/pre-commit'.format( + empty_git_dir, + ) def test_create_pre_commit(empty_git_dir): diff --git a/tests/hooks_workspace_test.py b/tests/hooks_workspace_test.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/repo_installer_test.py b/tests/repo_installer_test.py index 00ad5b81..12c89c33 100644 --- a/tests/repo_installer_test.py +++ b/tests/repo_installer_test.py @@ -2,31 +2,46 @@ import os import jsonschema import pytest -from plumbum import local +from pre_commit import git import pre_commit.constants as C from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA -from pre_commit.repo_installer import create_repo_in_env -from pre_commit.repo_installer import install_pre_commit +from pre_commit.repo_installer import RepoInstaller -def get_sha(git_repo): - with local.cwd(git_repo): - return (local['git']['log', '--format="%H"'] | local['head']['-n1'])().strip('"\n') +@pytest.fixture +def dummy_repo_config(dummy_git_repo): + # This is not a valid config, but it is pretty close + return { + 'repo': dummy_git_repo, + 'sha': git.get_head_sha(dummy_git_repo), + 'hooks': [], + } + @pytest.mark.integration -def test_create_repo_in_env(empty_git_dir, dummy_git_repo): - sha = get_sha(dummy_git_repo) - create_repo_in_env(dummy_git_repo, sha) +def test_create_repo_in_env(dummy_repo_config, dummy_git_repo): + repo_installer = RepoInstaller(dummy_repo_config) + repo_installer.create() - assert os.path.exists(os.path.join(dummy_git_repo, C.PRE_COMMIT_DIR, sha)) + assert os.path.exists( + os.path.join(dummy_git_repo, C.HOOKS_WORKSPACE, repo_installer.sha), + ) @pytest.mark.integration -def test_install_python_repo_in_env(empty_git_dir, python_pre_commit_git_repo): - sha = get_sha(python_pre_commit_git_repo) - install_pre_commit(python_pre_commit_git_repo, sha) +def test_install_python_repo_in_env(python_pre_commit_git_repo, config_for_python_pre_commit_git_repo): + repo_installer = RepoInstaller(config_for_python_pre_commit_git_repo) + # TODO: do we need create here? + repo_installer.install() - assert os.path.exists(os.path.join(python_pre_commit_git_repo, C.PRE_COMMIT_DIR, sha, 'py_env')) + assert os.path.exists( + os.path.join( + python_pre_commit_git_repo, + C.HOOKS_WORKSPACE, + repo_installer.sha, + 'py_env', + ), + ) @pytest.fixture @@ -34,28 +49,14 @@ def simple_config(python_pre_commit_git_repo): config = [ { 'repo': python_pre_commit_git_repo, - 'sha': get_sha(python_pre_commit_git_repo), + 'sha': git.get_head_sha(python_pre_commit_git_repo), 'hooks': [ { 'id': 'foo', 'files': '*.py', - } + }, ], }, ] jsonschema.validate(config, CONFIG_JSON_SCHEMA) return config - - -@pytest.mark.integration -def test_install_config(empty_git_dir, python_pre_commit_git_repo, simple_config): - for repo in simple_config: - install_pre_commit(repo['repo'], repo['sha']) - - assert os.path.exists( - os.path.join( - python_pre_commit_git_repo, - C.PRE_COMMIT_DIR, simple_config[0]['sha'], - 'py_env', - ), - ) \ No newline at end of file