From 9c3bbecab8768f82a963f3b4bec9c98ab20abec9 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 2 Sep 2017 19:49:23 -0700 Subject: [PATCH] Add new docker_image language type. `docker_image` is intended to be a lightweight hook type similar to system / script which allows one to use an existing docker image which provides a hook. --- pre_commit/languages/all.py | 2 ++ pre_commit/languages/docker.py | 24 ++++++++++--------- pre_commit/languages/docker_image.py | 19 +++++++++++++++ pre_commit/languages/helpers.py | 4 ++++ pre_commit/languages/pcre.py | 6 +---- pre_commit/languages/script.py | 6 +---- pre_commit/languages/system.py | 6 +---- pre_commit/repository.py | 4 ++-- .../.pre-commit-hooks.yaml | 8 +++++++ tests/repository_test.py | 12 ++++++++++ 10 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 pre_commit/languages/docker_image.py create mode 100644 testing/resources/docker_image_hooks_repo/.pre-commit-hooks.yaml diff --git a/pre_commit/languages/all.py b/pre_commit/languages/all.py index 5de57fb8..67b7ddea 100644 --- a/pre_commit/languages/all.py +++ b/pre_commit/languages/all.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals from pre_commit.languages import docker +from pre_commit.languages import docker_image from pre_commit.languages import golang from pre_commit.languages import node from pre_commit.languages import pcre @@ -49,6 +50,7 @@ from pre_commit.languages import system languages = { 'docker': docker, + 'docker_image': docker_image, 'golang': golang, 'node': node, 'pcre': pcre, diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py index a9a0d342..3dddf618 100644 --- a/pre_commit/languages/docker.py +++ b/pre_commit/languages/docker.py @@ -77,6 +77,16 @@ def install_environment( os.mkdir(directory) +def docker_cmd(): + return ( + 'docker', 'run', + '--rm', + '-u', '{}:{}'.format(os.getuid(), os.getgid()), + '-v', '{}:/src:rw'.format(os.getcwd()), + '--workdir', '/src', + ) + + def run_hook(repo_cmd_runner, hook, file_args): # pragma: windows no cover assert_docker_available() # Rebuild the docker image in case it has gone missing, as many people do @@ -84,16 +94,8 @@ def run_hook(repo_cmd_runner, hook, file_args): # pragma: windows no cover build_docker_image(repo_cmd_runner, pull=False) hook_cmd = helpers.to_cmd(hook) - entry_executable, cmd_rest = hook_cmd[0], hook_cmd[1:] - - cmd = ( - 'docker', 'run', - '--rm', - '-u', '{}:{}'.format(os.getuid(), os.getgid()), - '-v', '{}:/src:rw'.format(os.getcwd()), - '--workdir', '/src', - '--entrypoint', entry_executable, - docker_tag(repo_cmd_runner), - ) + cmd_rest + entry_exe, cmd_rest = hook_cmd[0], hook_cmd[1:] + entry_tag = ('--entrypoint', entry_exe, docker_tag(repo_cmd_runner)) + cmd = docker_cmd() + entry_tag + cmd_rest return xargs(cmd, file_args) diff --git a/pre_commit/languages/docker_image.py b/pre_commit/languages/docker_image.py new file mode 100644 index 00000000..a6f89e3f --- /dev/null +++ b/pre_commit/languages/docker_image.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import +from __future__ import unicode_literals + +from pre_commit.languages import helpers +from pre_commit.languages.docker import assert_docker_available +from pre_commit.languages.docker import docker_cmd +from pre_commit.xargs import xargs + + +ENVIRONMENT_DIR = None +get_default_version = helpers.basic_get_default_version +healthy = helpers.basic_healthy +install_environment = helpers.no_install + + +def run_hook(repo_cmd_runner, hook, file_args): # pragma: windows no cover + assert_docker_available() + cmd = docker_cmd() + helpers.to_cmd(hook) + return xargs(cmd, file_args) diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py index 930a0755..30082d6b 100644 --- a/pre_commit/languages/helpers.py +++ b/pre_commit/languages/helpers.py @@ -41,3 +41,7 @@ def basic_get_default_version(): def basic_healthy(repo_cmd_runner, language_version): return True + + +def no_install(repo_cmd_runner, version, additional_dependencies): + raise AssertionError('This type is not installable') diff --git a/pre_commit/languages/pcre.py b/pre_commit/languages/pcre.py index 6ef373f0..eaacc110 100644 --- a/pre_commit/languages/pcre.py +++ b/pre_commit/languages/pcre.py @@ -10,11 +10,7 @@ ENVIRONMENT_DIR = None GREP = 'ggrep' if sys.platform == 'darwin' else 'grep' get_default_version = helpers.basic_get_default_version healthy = helpers.basic_healthy - - -def install_environment(repo_cmd_runner, version, additional_dependencies): - """Installation for pcre type is a noop.""" - raise AssertionError('Cannot install pcre repo.') +install_environment = helpers.no_install def run_hook(repo_cmd_runner, hook, file_args): diff --git a/pre_commit/languages/script.py b/pre_commit/languages/script.py index 0bbb3091..8c3b0c56 100644 --- a/pre_commit/languages/script.py +++ b/pre_commit/languages/script.py @@ -7,11 +7,7 @@ from pre_commit.xargs import xargs ENVIRONMENT_DIR = None get_default_version = helpers.basic_get_default_version healthy = helpers.basic_healthy - - -def install_environment(repo_cmd_runner, version, additional_dependencies): - """Installation for script type is a noop.""" - raise AssertionError('Cannot install script repo.') +install_environment = helpers.no_install def run_hook(repo_cmd_runner, hook, file_args): diff --git a/pre_commit/languages/system.py b/pre_commit/languages/system.py index 1f1688d8..693a1601 100644 --- a/pre_commit/languages/system.py +++ b/pre_commit/languages/system.py @@ -7,11 +7,7 @@ from pre_commit.xargs import xargs ENVIRONMENT_DIR = None get_default_version = helpers.basic_get_default_version healthy = helpers.basic_healthy - - -def install_environment(repo_cmd_runner, version, additional_dependencies): - """Installation for system type is a noop.""" - raise AssertionError('Cannot install system repo.') +install_environment = helpers.no_install def run_hook(repo_cmd_runner, hook, file_args): diff --git a/pre_commit/repository.py b/pre_commit/repository.py index 18f902cb..675c4716 100644 --- a/pre_commit/repository.py +++ b/pre_commit/repository.py @@ -202,8 +202,8 @@ class LocalRepository(Repository): 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 + # pcre / script / system / docker_image do not have environments so + # they work out of the current directory if language.ENVIRONMENT_DIR is None: return PrefixedCommandRunner(git.get_root()) else: diff --git a/testing/resources/docker_image_hooks_repo/.pre-commit-hooks.yaml b/testing/resources/docker_image_hooks_repo/.pre-commit-hooks.yaml new file mode 100644 index 00000000..1b385aa1 --- /dev/null +++ b/testing/resources/docker_image_hooks_repo/.pre-commit-hooks.yaml @@ -0,0 +1,8 @@ +- id: echo-entrypoint + name: echo (via --entrypoint) + language: docker_image + entry: --entrypoint echo cogniteev/echo +- id: echo-cmd + name: echo (via cmd) + language: docker_image + entry: cogniteev/echo echo diff --git a/tests/repository_test.py b/tests/repository_test.py index 9ad28862..ae924a76 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -164,6 +164,18 @@ def test_run_a_failing_docker_hook(tempdir_factory, store): ) +@skipif_slowtests_false +@skipif_cant_run_docker +@pytest.mark.integration +@pytest.mark.parametrize('hook_id', ('echo-entrypoint', 'echo-cmd')) +def test_run_a_docker_image_hook(tempdir_factory, store, hook_id): + _test_hook_repo( + tempdir_factory, store, 'docker_image_hooks_repo', + hook_id, + ['Hello World from docker'], b'Hello World from docker\n', + ) + + @skipif_slowtests_false @xfailif_windows_no_node @pytest.mark.integration