mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-04-15 18:11:48 +04:00
fix: fix Docker path mounting
Currently pre-commit mounts the current directory to /src and uses current directory name as mount base. However this does not work when pre-commit is run inside the container on some mounted path already, because mount points are relative to the host, not to the container. See https://gist.github.com/dpfoose/f96d4e4b76c2e01265619d545b77987a Fixes #1387
This commit is contained in:
parent
24d9dc7469
commit
6d31cff55f
2 changed files with 72 additions and 1 deletions
|
|
@ -8,6 +8,7 @@ from pre_commit.hook import Hook
|
||||||
from pre_commit.languages import helpers
|
from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
|
from pre_commit.util import translate_path
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'docker'
|
ENVIRONMENT_DIR = 'docker'
|
||||||
PRE_COMMIT_LABEL = 'PRE_COMMIT'
|
PRE_COMMIT_LABEL = 'PRE_COMMIT'
|
||||||
|
|
@ -73,7 +74,7 @@ def docker_cmd() -> Tuple[str, ...]: # pragma: win32 no cover
|
||||||
# https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from
|
# https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from
|
||||||
# The `Z` option tells Docker to label the content with a private
|
# The `Z` option tells Docker to label the content with a private
|
||||||
# unshared label. Only the current container can use a private volume.
|
# unshared label. Only the current container can use a private volume.
|
||||||
'-v', f'{os.getcwd()}:/src:rw,Z',
|
'-v', f'{translate_path(os.getcwd())}:/src:rw,Z',
|
||||||
'--workdir', '/src',
|
'--workdir', '/src',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
import errno
|
import errno
|
||||||
import functools
|
import functools
|
||||||
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import socket
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -268,3 +271,70 @@ def rmtree(path: str) -> None:
|
||||||
def parse_version(s: str) -> Tuple[int, ...]:
|
def parse_version(s: str) -> Tuple[int, ...]:
|
||||||
"""poor man's version comparison"""
|
"""poor man's version comparison"""
|
||||||
return tuple(int(p) for p in s.split('.'))
|
return tuple(int(p) for p in s.split('.'))
|
||||||
|
|
||||||
|
|
||||||
|
def in_docker() -> bool:
|
||||||
|
"""
|
||||||
|
Check if running in Docker
|
||||||
|
:return: Whether or not this is running in Docker container
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open('/proc/1/cgroup') as cgroup_file:
|
||||||
|
return 'docker' in cgroup_file.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def translate_path(path: str) -> str:
|
||||||
|
"""
|
||||||
|
Method to get the right path considering it can be mounted in Docker
|
||||||
|
already.
|
||||||
|
:param path: A string representing a path within the container
|
||||||
|
:return: A string representing a path on the host (or the original
|
||||||
|
path if the path is not in a bound volume)
|
||||||
|
"""
|
||||||
|
if not in_docker():
|
||||||
|
return path
|
||||||
|
binds = get_binds()
|
||||||
|
if path in binds.keys():
|
||||||
|
return binds[path]
|
||||||
|
exps = ['(%s)/(.*)' % key for key in binds.keys()]
|
||||||
|
for exp in exps:
|
||||||
|
result = re.search(exp, path)
|
||||||
|
if result:
|
||||||
|
return f'{binds[result.group(1)]}/{result.group(2)}'
|
||||||
|
raise ValueError(
|
||||||
|
f'Path {path} not present in a bind mount. ' +
|
||||||
|
'Volume mount will fail when running this in Docker.',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_container() -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Will raise ValueError if there is no container with the same hostname as
|
||||||
|
the environment this is running in.
|
||||||
|
Which indicates that this is not a docker container, or that
|
||||||
|
/var/run/docker.sock is not bind mounted to /var/run/docker.sock on the
|
||||||
|
host (i.e. this is a container which is also a docker host).
|
||||||
|
:return: A dictionary containing information about the container this
|
||||||
|
is running in obtained using docker api
|
||||||
|
"""
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(('docker', 'inspect', hostname))
|
||||||
|
except CalledProcessError:
|
||||||
|
raise ValueError('Not running in Docker container')
|
||||||
|
|
||||||
|
return json.loads(output)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_binds() -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
:return: A dictionary with paths in the container as keys and paths
|
||||||
|
on the host as values
|
||||||
|
"""
|
||||||
|
container = get_current_container()
|
||||||
|
return {
|
||||||
|
bind.split(':')[1]: bind.split(':')[0]
|
||||||
|
for bind in container['HostConfig']['Binds']
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue