diff --git a/pre_commit/commands/install_uninstall.py b/pre_commit/commands/install_uninstall.py index 249c87d0..ab91eacf 100644 --- a/pre_commit/commands/install_uninstall.py +++ b/pre_commit/commands/install_uninstall.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals import io import os import os.path -import pkg_resources import stat +from pre_commit.util import resource_filename + # This is used to identify the hook file we install PREVIOUS_IDENTIFYING_HASHES = [ @@ -36,9 +37,7 @@ def make_executable(filename): def install(runner, overwrite=False): """Install the pre-commit hooks.""" - pre_commit_file = pkg_resources.resource_filename( - 'pre_commit', 'resources/pre-commit-hook', - ) + pre_commit_file = resource_filename('pre-commit-hook') # If we have an existing hook, move it to pre-commit.legacy if ( diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py index b4a3be06..2f4b9931 100644 --- a/pre_commit/languages/ruby.py +++ b/pre_commit/languages/ruby.py @@ -6,6 +6,8 @@ import io from pre_commit.languages import helpers from pre_commit.prefixed_command_runner import CalledProcessError from pre_commit.util import clean_path_on_failure +from pre_commit.util import resource_filename +from pre_commit.util import tarfile_open ENVIRONMENT_DIR = 'rbenv' @@ -23,22 +25,18 @@ def in_env(repo_cmd_runner): def _install_rbenv(repo_cmd_runner, version='default'): - repo_cmd_runner.run([ - 'git', 'clone', 'git://github.com/sstephenson/rbenv', '{prefix}rbenv', - ]) + with tarfile_open(resource_filename('rbenv.tar.gz')) as tf: + tf.extractall(repo_cmd_runner.path('.')) # Only install ruby-build if the version is specified if version != 'default': # ruby-download - repo_cmd_runner.run([ - 'git', 'clone', 'git://github.com/garnieretienne/rvm-download', - '{prefix}rbenv/plugins/ruby-download', - ]) + with tarfile_open(resource_filename('ruby-download.tar.gz')) as tf: + tf.extractall(repo_cmd_runner.path('rbenv', 'plugins')) + # ruby-build - repo_cmd_runner.run([ - 'git', 'clone', 'git://github.com/sstephenson/ruby-build', - '{prefix}rbenv/plugins/ruby-build', - ]) + with tarfile_open(resource_filename('ruby-build.tar.gz')) as tf: + tf.extractall(repo_cmd_runner.path('rbenv', 'plugins')) activate_path = repo_cmd_runner.path('rbenv', 'bin', 'activate') with io.open(activate_path, 'w') as activate_file: diff --git a/pre_commit/make_archives.py b/pre_commit/make_archives.py new file mode 100644 index 00000000..4bae338c --- /dev/null +++ b/pre_commit/make_archives.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals + +import os.path +import shutil +from plumbum import local + +from pre_commit.util import tarfile_open +from pre_commit.util import tmpdir + + +# This is a script for generating the tarred resources for git repo +# dependencies. Currently it's just for "vendoring" ruby support packages. + + +REPOS = ( + ('rbenv', 'git://github.com/sstephenson/rbenv', '13a474c'), + ('ruby-build', 'git://github.com/sstephenson/ruby-build', 'd3d5fe0'), + ( + 'ruby-download', + 'git://github.com/garnieretienne/rvm-download', + 'f2e9f1e', + ), +) + + +RESOURCES_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'resources') +) + + +def make_archive(name, repo, ref, destdir): + """Makes an archive of a repository in the given destdir. + + :param text name: Name to give the archive. For instance foo. The file + that is created will be called foo.tar.gz. + :param text repo: Repository to clone. + :param text ref: Tag/SHA/branch to check out. + :param text destdir: Directory to place archives in. + """ + output_path = os.path.join(destdir, name + '.tar.gz') + with tmpdir() as tempdir: + # Clone the repository to the temporary directory + local['git']('clone', repo, tempdir) + with local.cwd(tempdir): + local['git']('checkout', ref) + + # We don't want the '.git' directory + shutil.rmtree(os.path.join(tempdir, '.git')) + + # XXX: py2.6 derps if filename is unicode while writing + # XXX: str() is used to preserve behavior in py3 + with tarfile_open(str(output_path), 'w|gz') as tf: + tf.add(tempdir, name) + + return output_path + + +def main(): + for archive_name, repo, ref in REPOS: + print('Making {0}.tar.gz for {1}@{2}'.format(archive_name, repo, ref)) + make_archive(archive_name, repo, ref, RESOURCES_DIR) + + +if __name__ == '__main__': + exit(main()) diff --git a/pre_commit/resources/rbenv.tar.gz b/pre_commit/resources/rbenv.tar.gz new file mode 100644 index 00000000..bd517311 Binary files /dev/null and b/pre_commit/resources/rbenv.tar.gz differ diff --git a/pre_commit/resources/ruby-build.tar.gz b/pre_commit/resources/ruby-build.tar.gz new file mode 100644 index 00000000..10368199 Binary files /dev/null and b/pre_commit/resources/ruby-build.tar.gz differ diff --git a/pre_commit/resources/ruby-download.tar.gz b/pre_commit/resources/ruby-download.tar.gz new file mode 100644 index 00000000..24bc6479 Binary files /dev/null and b/pre_commit/resources/ruby-download.tar.gz differ diff --git a/pre_commit/util.py b/pre_commit/util.py index 4b625c27..a46de364 100644 --- a/pre_commit/util.py +++ b/pre_commit/util.py @@ -5,8 +5,11 @@ import functools import hashlib import os import os.path +import pkg_resources import shutil import sys +import tarfile +import tempfile def memoize_by_cwd(func): @@ -65,3 +68,32 @@ def hex_md5(s): :param text s: """ return hashlib.md5(s.encode('utf-8')).hexdigest() + + +@contextlib.contextmanager +def tarfile_open(*args, **kwargs): + """Compatibility layer because python2.6""" + tf = tarfile.open(*args, **kwargs) + try: + yield tf + finally: + tf.close() + + +@contextlib.contextmanager +def tmpdir(): + """Contextmanager to create a temporary directory. It will be cleaned up + afterwards. + """ + tempdir = tempfile.mkdtemp() + try: + yield tempdir + finally: + shutil.rmtree(tempdir) + + +def resource_filename(filename): + return pkg_resources.resource_filename( + 'pre_commit', + os.path.join('resources', filename), + ) diff --git a/setup.py b/setup.py index 25afb566..f7550cf6 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,10 @@ setup( packages=find_packages('.', exclude=('tests*', 'testing*')), package_data={ 'pre_commit': [ - 'resources/pre-commit-hook' + 'resources/pre-commit-hook', + 'resources/rbenv.tar.gz', + 'resources/ruby-build.tar.gz', + 'resources/ruby-download.tar.gz', ] }, install_requires=[ diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py index bb511384..ae9dddfb 100644 --- a/tests/commands/install_uninstall_test.py +++ b/tests/commands/install_uninstall_test.py @@ -5,7 +5,6 @@ import io import os import os.path import re -import pkg_resources import subprocess import stat from plumbum import local @@ -18,6 +17,7 @@ from pre_commit.commands.install_uninstall import is_previous_pre_commit from pre_commit.commands.install_uninstall import make_executable from pre_commit.commands.install_uninstall import uninstall from pre_commit.runner import Runner +from pre_commit.util import resource_filename from testing.fixtures import git_dir from testing.fixtures import make_consuming_repo @@ -27,11 +27,7 @@ def test_is_not_our_pre_commit(): def test_is_our_pre_commit(): - assert is_our_pre_commit( - pkg_resources.resource_filename( - 'pre_commit', 'resources/pre-commit-hook', - ) - ) is True + assert is_our_pre_commit(resource_filename('pre-commit-hook')) def test_is_not_previous_pre_commit(): @@ -39,11 +35,7 @@ def test_is_not_previous_pre_commit(): def test_is_also_not_previous_pre_commit(): - assert is_previous_pre_commit( - pkg_resources.resource_filename( - 'pre_commit', 'resources/pre-commit-hook', - ) - ) is False + assert not is_previous_pre_commit(resource_filename('pre-commit-hook')) def test_is_previous_pre_commit(in_tmpdir): @@ -60,9 +52,7 @@ def test_install_pre_commit(tmpdir_factory): assert ret == 0 assert os.path.exists(runner.pre_commit_path) pre_commit_contents = io.open(runner.pre_commit_path).read() - pre_commit_script = pkg_resources.resource_filename( - 'pre_commit', 'resources/pre-commit-hook', - ) + pre_commit_script = resource_filename('pre-commit-hook') expected_contents = io.open(pre_commit_script).read() assert pre_commit_contents == expected_contents stat_result = os.stat(runner.pre_commit_path) @@ -317,9 +307,7 @@ def test_replace_old_commit_script(tmpdir_factory): # Install a script that looks like our old script pre_commit_contents = io.open( - pkg_resources.resource_filename( - 'pre_commit', 'resources/pre-commit-hook', - ) + resource_filename('pre-commit-hook'), ).read() new_contents = pre_commit_contents.replace( IDENTIFYING_HASH, PREVIOUS_IDENTIFYING_HASHES[-1], diff --git a/tests/languages/ruby_test.py b/tests/languages/ruby_test.py index d55b36b0..3ffb4019 100644 --- a/tests/languages/ruby_test.py +++ b/tests/languages/ruby_test.py @@ -3,16 +3,12 @@ from __future__ import unicode_literals import os.path from pre_commit.languages.ruby import _install_rbenv -from testing.util import skipif_slowtests_false -@skipif_slowtests_false def test_install_rbenv(cmd_runner): _install_rbenv(cmd_runner) # Should have created rbenv directory assert os.path.exists(cmd_runner.path('rbenv')) - # It should be a git checkout - assert os.path.exists(cmd_runner.path('rbenv', '.git')) # We should have created our `activate` script activate_path = cmd_runner.path('rbenv', 'bin', 'activate') assert os.path.exists(activate_path) @@ -27,7 +23,6 @@ def test_install_rbenv(cmd_runner): ) -@skipif_slowtests_false def test_install_rbenv_with_version(cmd_runner): _install_rbenv(cmd_runner, version='1.9.3p547') diff --git a/tests/make_archives_test.py b/tests/make_archives_test.py new file mode 100644 index 00000000..290a0caf --- /dev/null +++ b/tests/make_archives_test.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import +from __future__ import unicode_literals + +import mock +import os.path +import pytest +from plumbum import local + +from pre_commit import make_archives +from pre_commit.util import tarfile_open +from testing.fixtures import git_dir +from testing.util import get_head_sha +from testing.util import skipif_slowtests_false + + +def test_make_archive(tmpdir_factory): + output_dir = tmpdir_factory.get() + git_path = git_dir(tmpdir_factory) + # Add a files to the git directory + with local.cwd(git_path): + local['touch']('foo') + local['git']('add', '.') + local['git']('commit', '-m', 'foo') + # We'll use this sha + head_sha = get_head_sha('.') + # And check that this file doesn't exist + local['touch']('bar') + local['git']('add', '.') + local['git']('commit', '-m', 'bar') + + # Do the thing + archive_path = make_archives.make_archive( + 'foo', git_path, head_sha, output_dir, + ) + + assert archive_path == os.path.join(output_dir, 'foo.tar.gz') + assert os.path.exists(archive_path) + + extract_dir = tmpdir_factory.get() + + # Extract the tar + with tarfile_open(archive_path) as tf: + tf.extractall(extract_dir) + + # Verify the contents of the tar + assert os.path.exists(os.path.join(extract_dir, 'foo')) + assert os.path.exists(os.path.join(extract_dir, 'foo', 'foo')) + assert not os.path.exists(os.path.join(extract_dir, 'foo', '.git')) + assert not os.path.exists(os.path.join(extract_dir, 'foo', 'bar')) + + +@skipif_slowtests_false +@pytest.mark.integration +def test_main(tmpdir_factory): + path = tmpdir_factory.get() + + # Don't actually want to make these in the current repo + with mock.patch.object(make_archives, 'RESOURCES_DIR', path): + make_archives.main() + + for archive, _, _ in make_archives.REPOS: + assert os.path.exists(os.path.join(path, archive + '.tar.gz')) diff --git a/tests/util_test.py b/tests/util_test.py index e406d604..b34b47ab 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -12,6 +12,7 @@ from pre_commit.util import clean_path_on_failure from pre_commit.util import entry from pre_commit.util import memoize_by_cwd from pre_commit.util import shell_escape +from pre_commit.util import tmpdir @pytest.fixture @@ -112,3 +113,9 @@ def test_clean_path_on_failure_cleans_for_system_exit(in_tmpdir): ) def test_shell_escape(input_str, expected): assert shell_escape(input_str) == expected + + +def test_tmpdir(): + with tmpdir() as tempdir: + assert os.path.exists(tempdir) + assert not os.path.exists(tempdir)