diff options
| author | Aanand Prasad <aanand.prasad@gmail.com> | 2015-08-26 16:07:12 +0100 |
|---|---|---|
| committer | Aanand Prasad <aanand.prasad@gmail.com> | 2015-08-26 19:14:09 +0100 |
| commit | 20e142fe7878800830acb216625487145e4a1d96 (patch) | |
| tree | f0b64974e83897821bb9bd5ff8017be0b2ce700e /docker/utils | |
| parent | d60cb3172e767d77cb55c8b183ac0f7cea658d5f (diff) | |
| download | docker-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__.py | 2 | ||||
| -rw-r--r-- | docker/utils/utils.py | 95 |
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 |
