summaryrefslogtreecommitdiff
path: root/docker/utils
diff options
context:
space:
mode:
authorAanand Prasad <aanand.prasad@gmail.com>2015-08-26 16:07:12 +0100
committerAanand Prasad <aanand.prasad@gmail.com>2015-08-26 19:14:09 +0100
commit20e142fe7878800830acb216625487145e4a1d96 (patch)
treef0b64974e83897821bb9bd5ff8017be0b2ce700e /docker/utils
parentd60cb3172e767d77cb55c8b183ac0f7cea658d5f (diff)
downloaddocker-py-20e142fe7878800830acb216625487145e4a1d96.tar.gz
Better support for .dockerignore
- Support all basic pattern forms: file, directory, *, ?, ! - Fix handling of wildcard patterns and subdirectories - `*/a.py` should match `foo/a.py`, but not `foo/bar/a.py` - Fix handling of directory patterns with a trailing slash - make sure they're handled equivalently to those without one - Fix handling of custom Dockerfiles - make sure they go in the tarball Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
Diffstat (limited to 'docker/utils')
-rw-r--r--docker/utils/__init__.py2
-rw-r--r--docker/utils/utils.py95
2 files changed, 70 insertions, 27 deletions
diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py
index 6189ed8..deab01d 100644
--- a/docker/utils/__init__.py
+++ b/docker/utils/__init__.py
@@ -1,6 +1,6 @@
from .utils import (
compare_version, convert_port_bindings, convert_volume_binds,
- mkbuildcontext, tar, parse_repository_tag, parse_host,
+ mkbuildcontext, tar, exclude_paths, parse_repository_tag, parse_host,
kwargs_from_env, convert_filters, create_host_config,
create_container_config, parse_bytes, ping_registry, parse_env_file
) # flake8: noqa
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 8f69950..4c05e0c 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -66,39 +66,82 @@ def mkbuildcontext(dockerfile):
return f
-def fnmatch_any(relpath, patterns):
- return any([fnmatch(relpath, pattern) for pattern in patterns])
-
-
-def tar(path, exclude=None):
+def tar(path, exclude=None, dockerfile=None):
f = tempfile.NamedTemporaryFile()
t = tarfile.open(mode='w', fileobj=f)
- for dirpath, dirnames, filenames in os.walk(path):
- relpath = os.path.relpath(dirpath, path)
- if relpath == '.':
- relpath = ''
- if exclude is None:
- fnames = filenames
- else:
- dirnames[:] = [d for d in dirnames
- if not fnmatch_any(os.path.join(relpath, d),
- exclude)]
- fnames = [name for name in filenames
- if not fnmatch_any(os.path.join(relpath, name),
- exclude)]
- dirnames.sort()
- for name in sorted(fnames):
- arcname = os.path.join(relpath, name)
- t.add(os.path.join(path, arcname), arcname=arcname)
- for name in dirnames:
- arcname = os.path.join(relpath, name)
- t.add(os.path.join(path, arcname),
- arcname=arcname, recursive=False)
+
+ root = os.path.abspath(path)
+ exclude = exclude or []
+
+ for path in sorted(exclude_paths(root, exclude, dockerfile=dockerfile)):
+ t.add(os.path.join(root, path), arcname=path, recursive=False)
+
t.close()
f.seek(0)
return f
+def exclude_paths(root, patterns, dockerfile=None):
+ """
+ Given a root directory path and a list of .dockerignore patterns, return
+ an iterator of all paths (both regular files and directories) in the root
+ directory that do *not* match any of the patterns.
+
+ All paths returned are relative to the root.
+ """
+ if dockerfile is None:
+ dockerfile = 'Dockerfile'
+
+ exceptions = [p for p in patterns if p.startswith('!')]
+
+ include_patterns = [p[1:] for p in exceptions]
+ include_patterns += [dockerfile, '.dockerignore']
+
+ exclude_patterns = list(set(patterns) - set(exceptions))
+
+ all_paths = get_paths(root)
+
+ # Remove all paths that are matched by any exclusion pattern
+ paths = [
+ p for p in all_paths
+ if not any(match_path(p, pattern) for pattern in exclude_patterns)
+ ]
+
+ # Add back the set of paths that are matched by any inclusion pattern.
+ # Include parent dirs - if we add back 'foo/bar', add 'foo' as well
+ for p in all_paths:
+ if any(match_path(p, pattern) for pattern in include_patterns):
+ components = p.split('/')
+ paths += [
+ '/'.join(components[:end])
+ for end in range(1, len(components)+1)
+ ]
+
+ return set(paths)
+
+
+def get_paths(root):
+ paths = []
+
+ for parent, dirs, files in os.walk(root, followlinks=False):
+ parent = os.path.relpath(parent, root)
+ if parent == '.':
+ parent = ''
+ for path in dirs:
+ paths.append(os.path.join(parent, path))
+ for path in files:
+ paths.append(os.path.join(parent, path))
+
+ return paths
+
+
+def match_path(path, pattern):
+ pattern = pattern.rstrip('/')
+ pattern_components = pattern.split('/')
+ path_components = path.split('/')[:len(pattern_components)]
+ return fnmatch('/'.join(path_components), pattern)
+
+
def compare_version(v1, v2):
"""Compare docker versions