mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-01-14 13:00:10 -06:00
Local repositories clone a blank repo
This commit is contained in:
@@ -32,7 +32,7 @@ def _update_repo(repo_config, runner, tags_only):
|
||||
"""
|
||||
repo = Repository.create(repo_config, runner.store)
|
||||
|
||||
with cwd(repo.repo_path_getter.repo_path):
|
||||
with cwd(repo._repo_path):
|
||||
cmd_output('git', 'fetch')
|
||||
tag_cmd = ('git', 'describe', 'origin/master', '--tags')
|
||||
if tags_only:
|
||||
|
||||
@@ -13,15 +13,14 @@ logger = logging.getLogger('pre_commit')
|
||||
|
||||
|
||||
class Manifest(object):
|
||||
def __init__(self, repo_path_getter, repo_url):
|
||||
self.repo_path_getter = repo_path_getter
|
||||
def __init__(self, repo_path, repo_url):
|
||||
self.repo_path = repo_path
|
||||
self.repo_url = repo_url
|
||||
|
||||
@cached_property
|
||||
def manifest_contents(self):
|
||||
repo_path = self.repo_path_getter.repo_path
|
||||
default_path = os.path.join(repo_path, C.MANIFEST_FILE)
|
||||
legacy_path = os.path.join(repo_path, C.MANIFEST_FILE_LEGACY)
|
||||
default_path = os.path.join(self.repo_path, C.MANIFEST_FILE)
|
||||
legacy_path = os.path.join(self.repo_path, C.MANIFEST_FILE_LEGACY)
|
||||
if os.path.exists(legacy_path) and not os.path.exists(default_path):
|
||||
logger.warning(
|
||||
'{} uses legacy {} to provide hooks.\n'
|
||||
|
||||
@@ -96,40 +96,34 @@ def _install_all(venvs, repo_url):
|
||||
|
||||
|
||||
class Repository(object):
|
||||
def __init__(self, repo_config, repo_path_getter):
|
||||
def __init__(self, repo_config, store):
|
||||
self.repo_config = repo_config
|
||||
self.repo_path_getter = repo_path_getter
|
||||
self.store = store
|
||||
self.__installed = False
|
||||
|
||||
@classmethod
|
||||
def create(cls, config, store):
|
||||
if is_local_hooks(config):
|
||||
return LocalRepository(config)
|
||||
return LocalRepository(config, store)
|
||||
else:
|
||||
repo_path_getter = store.get_repo_path_getter(
|
||||
config['repo'], config['sha']
|
||||
)
|
||||
return cls(config, repo_path_getter)
|
||||
return cls(config, store)
|
||||
|
||||
@cached_property
|
||||
def _repo_path(self):
|
||||
return self.store.clone(
|
||||
self.repo_config['repo'], self.repo_config['sha'],
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def _cmd_runner(self):
|
||||
return PrefixedCommandRunner(self.repo_path_getter.repo_path)
|
||||
return PrefixedCommandRunner(self._repo_path)
|
||||
|
||||
@cached_property
|
||||
def _venvs(self):
|
||||
deps_dict = defaultdict(_UniqueList)
|
||||
for _, hook in self.hooks:
|
||||
deps_dict[(hook['language'], hook['language_version'])].update(
|
||||
hook.get('additional_dependencies', []),
|
||||
)
|
||||
ret = []
|
||||
for (language, version), deps in deps_dict.items():
|
||||
ret.append((self._cmd_runner, language, version, deps))
|
||||
return tuple(ret)
|
||||
def _cmd_runner_from_deps(self, language_name, deps):
|
||||
return self._cmd_runner
|
||||
|
||||
@cached_property
|
||||
def manifest(self):
|
||||
return Manifest(self.repo_path_getter, self.repo_config['repo'])
|
||||
return Manifest(self._repo_path, self.repo_config['repo'])
|
||||
|
||||
@cached_property
|
||||
def hooks(self):
|
||||
@@ -160,6 +154,18 @@ class Repository(object):
|
||||
for hook in self.repo_config['hooks']
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def _venvs(self):
|
||||
deps_dict = defaultdict(_UniqueList)
|
||||
for _, hook in self.hooks:
|
||||
deps_dict[(hook['language'], hook['language_version'])].update(
|
||||
hook.get('additional_dependencies', []),
|
||||
)
|
||||
ret = []
|
||||
for (language, version), deps in deps_dict.items():
|
||||
ret.append((self._cmd_runner, language, version, deps))
|
||||
return tuple(ret)
|
||||
|
||||
def require_installed(self):
|
||||
if not self.__installed:
|
||||
_install_all(self._venvs, self.repo_config['repo'])
|
||||
@@ -168,19 +174,30 @@ class Repository(object):
|
||||
def run_hook(self, hook, file_args):
|
||||
"""Run a hook.
|
||||
|
||||
Args:
|
||||
hook - Hook dictionary
|
||||
file_args - List of files to run
|
||||
:param dict hook:
|
||||
:param tuple file_args: all the files to run the hook on
|
||||
"""
|
||||
self.require_installed()
|
||||
return languages[hook['language']].run_hook(
|
||||
self._cmd_runner, hook, file_args,
|
||||
)
|
||||
language_name = hook['language']
|
||||
deps = hook.get('additional_dependencies', [])
|
||||
cmd_runner = self._cmd_runner_from_deps(language_name, deps)
|
||||
return languages[language_name].run_hook(cmd_runner, hook, file_args)
|
||||
|
||||
|
||||
class LocalRepository(Repository):
|
||||
def __init__(self, repo_config):
|
||||
super(LocalRepository, self).__init__(repo_config, None)
|
||||
def _cmd_runner_from_deps(self, language_name, deps):
|
||||
"""local repositories have a cmd runner per hook"""
|
||||
language = languages[language_name]
|
||||
# pcre / script / system do not have environments so they work out
|
||||
# of the current directory
|
||||
if language.ENVIRONMENT_DIR is None:
|
||||
return PrefixedCommandRunner(git.get_root())
|
||||
else:
|
||||
return PrefixedCommandRunner(self.store.make_local(deps))
|
||||
|
||||
@cached_property
|
||||
def manifest(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@cached_property
|
||||
def hooks(self):
|
||||
@@ -190,12 +207,17 @@ class LocalRepository(Repository):
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def cmd_runner(self):
|
||||
return PrefixedCommandRunner(git.get_root())
|
||||
|
||||
@cached_property
|
||||
def manifest(self):
|
||||
raise NotImplementedError
|
||||
def _venvs(self):
|
||||
ret = []
|
||||
for _, hook in self.hooks:
|
||||
language = hook['language']
|
||||
version = hook['language_version']
|
||||
deps = hook.get('additional_dependencies', [])
|
||||
ret.append((
|
||||
self._cmd_runner_from_deps(language, deps),
|
||||
language, version, deps,
|
||||
))
|
||||
return tuple(ret)
|
||||
|
||||
|
||||
class _UniqueList(list):
|
||||
|
||||
1
pre_commit/resources/empty_template/.npmignore
Normal file
1
pre_commit/resources/empty_template/.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
*
|
||||
3
pre_commit/resources/empty_template/main.go
Normal file
3
pre_commit/resources/empty_template/main.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
4
pre_commit/resources/empty_template/package.json
Normal file
4
pre_commit/resources/empty_template/package.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "pre_commit_dummy_package",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'pre_commit_dummy_package'
|
||||
s.version = '0.0.0'
|
||||
s.authors = ['Anthony Sottile']
|
||||
end
|
||||
4
pre_commit/resources/empty_template/setup.py
Normal file
4
pre_commit/resources/empty_template/setup.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
setup(name='pre-commit-dummy-package', version='0.0.0')
|
||||
@@ -12,8 +12,10 @@ from cached_property import cached_property
|
||||
from pre_commit.prefixed_command_runner import PrefixedCommandRunner
|
||||
from pre_commit.util import clean_path_on_failure
|
||||
from pre_commit.util import cmd_output
|
||||
from pre_commit.util import copy_tree_to_path
|
||||
from pre_commit.util import cwd
|
||||
from pre_commit.util import no_git_env
|
||||
from pre_commit.util import resource_filename
|
||||
|
||||
|
||||
logger = logging.getLogger('pre_commit')
|
||||
@@ -35,16 +37,6 @@ def _get_default_directory():
|
||||
class Store(object):
|
||||
get_default_directory = staticmethod(_get_default_directory)
|
||||
|
||||
class RepoPathGetter(object):
|
||||
def __init__(self, repo, ref, store):
|
||||
self._repo = repo
|
||||
self._ref = ref
|
||||
self._store = store
|
||||
|
||||
@cached_property
|
||||
def repo_path(self):
|
||||
return self._store.clone(self._repo, self._ref)
|
||||
|
||||
def __init__(self, directory=None):
|
||||
if directory is None:
|
||||
directory = self.get_default_directory()
|
||||
@@ -91,45 +83,55 @@ class Store(object):
|
||||
|
||||
def require_created(self):
|
||||
"""Require the pre-commit file store to be created."""
|
||||
if self.__created:
|
||||
return
|
||||
if not self.__created:
|
||||
self._create()
|
||||
self.__created = True
|
||||
|
||||
self._create()
|
||||
self.__created = True
|
||||
|
||||
def clone(self, url, ref):
|
||||
"""Clone the given url and checkout the specific ref."""
|
||||
def _new_repo(self, repo, ref, make_strategy):
|
||||
self.require_created()
|
||||
|
||||
# Check if we already exist
|
||||
with sqlite3.connect(self.db_path) as db:
|
||||
result = db.execute(
|
||||
'SELECT path FROM repos WHERE repo = ? AND ref = ?',
|
||||
[url, ref],
|
||||
[repo, ref],
|
||||
).fetchone()
|
||||
if result:
|
||||
return result[0]
|
||||
|
||||
logger.info('Initializing environment for {}.'.format(url))
|
||||
logger.info('Initializing environment for {}.'.format(repo))
|
||||
|
||||
dir = tempfile.mkdtemp(prefix='repo', dir=self.directory)
|
||||
with clean_path_on_failure(dir):
|
||||
cmd_output(
|
||||
'git', 'clone', '--no-checkout', url, dir, env=no_git_env(),
|
||||
)
|
||||
with cwd(dir):
|
||||
cmd_output('git', 'reset', ref, '--hard', env=no_git_env())
|
||||
directory = tempfile.mkdtemp(prefix='repo', dir=self.directory)
|
||||
with clean_path_on_failure(directory):
|
||||
make_strategy(directory)
|
||||
|
||||
# Update our db with the created repo
|
||||
with sqlite3.connect(self.db_path) as db:
|
||||
db.execute(
|
||||
'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',
|
||||
[url, ref, dir],
|
||||
[repo, ref, directory],
|
||||
)
|
||||
return dir
|
||||
return directory
|
||||
|
||||
def get_repo_path_getter(self, repo, ref):
|
||||
return self.RepoPathGetter(repo, ref, self)
|
||||
def clone(self, repo, ref):
|
||||
"""Clone the given url and checkout the specific ref."""
|
||||
def clone_strategy(directory):
|
||||
cmd_output(
|
||||
'git', 'clone', '--no-checkout', repo, directory,
|
||||
env=no_git_env(),
|
||||
)
|
||||
with cwd(directory):
|
||||
cmd_output('git', 'reset', ref, '--hard', env=no_git_env())
|
||||
|
||||
return self._new_repo(repo, ref, clone_strategy)
|
||||
|
||||
def make_local(self, deps):
|
||||
def make_local_strategy(directory):
|
||||
copy_tree_to_path(resource_filename('empty_template'), directory)
|
||||
return self._new_repo(
|
||||
'local:{}'.format(','.join(sorted(deps))), 'N/A',
|
||||
make_local_strategy,
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def cmd_runner(self):
|
||||
|
||||
@@ -204,3 +204,21 @@ def rmtree(path):
|
||||
else:
|
||||
raise
|
||||
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
|
||||
|
||||
|
||||
def copy_tree_to_path(src_dir, dest_dir):
|
||||
"""Copies all of the things inside src_dir to an already existing dest_dir.
|
||||
|
||||
This looks eerily similar to shutil.copytree, but copytree has no option
|
||||
for not creating dest_dir.
|
||||
"""
|
||||
names = os.listdir(src_dir)
|
||||
|
||||
for name in names:
|
||||
srcname = os.path.join(src_dir, name)
|
||||
destname = os.path.join(dest_dir, name)
|
||||
|
||||
if os.path.isdir(srcname):
|
||||
shutil.copytree(srcname, destname)
|
||||
else:
|
||||
shutil.copy(srcname, destname)
|
||||
|
||||
Reference in New Issue
Block a user