From 1320659ba7fc94790bea9f42e0fd2e752a7cb883 Mon Sep 17 00:00:00 2001 From: Wes McNamee Date: Thu, 26 Oct 2017 10:58:19 -0700 Subject: [PATCH] Handle exclusions when ignoring files Always exclude Dockerfile and .dockerignore from being ignored. https://docs.docker.com/engine/reference/builder/#dockerignore-file > You can even use the .dockerignore file to exclude the Dockerfile and .dockerignore files. > These files are still sent to the daemon because it needs them to do its job. > But the ADD and COPY instructions do not copy them to the image. resolves #484 --- lib/docker/util.rb | 20 ++++++++-- spec/docker/util_spec.rb | 80 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/lib/docker/util.rb b/lib/docker/util.rb index 1065217c6..228f2ddc9 100644 --- a/lib/docker/util.rb +++ b/lib/docker/util.rb @@ -259,7 +259,7 @@ def build_config_header(credentials) end def glob_all_files(pattern) - Dir.glob(pattern, File::FNM_DOTMATCH) - ['..', '.'] + Dir.glob(pattern, File::FNM_DOTMATCH) end def remove_ignored_files!(directory, files) @@ -268,11 +268,23 @@ def remove_ignored_files!(directory, files) ignored_files(directory, ignore).each { |f| files.delete(f) } end - def ignored_files(directory, ignore_file) - patterns = File.read(ignore_file).split("\n").each(&:strip!) - patterns.reject! { |p| p.empty? || p.start_with?('#') } + def resolve_patterns(patterns, directory) patterns.map! { |p| File.join(directory, p) } patterns.map! { |p| File.directory?(p) ? "#{p}/**/*" : p } patterns.flat_map { |p| p =~ GLOB_WILDCARDS ? glob_all_files(p) : p } end + + def ignored_files(directory, ignore_file) + patterns = File.read(ignore_file).split("\n").each(&:strip!) + patterns.reject! { |p| p.empty? || p.start_with?('#') } + + exclusion_patterns = patterns.select { |p| p.start_with?('!') } + inclusion_patterns = patterns - exclusion_patterns + exclusion_patterns.map! { |p| p[1..-1] }.concat(%w(Dockerfile .dockerignore)) + + files_ignored = resolve_patterns(inclusion_patterns, directory) + files_excluded = resolve_patterns(exclusion_patterns, directory) + + files_ignored - files_excluded - [File.join(directory, '.'), File.join(directory, '..')] + end end diff --git a/spec/docker/util_spec.rb b/spec/docker/util_spec.rb index 5a2344fce..1bcbb2b70 100644 --- a/spec/docker/util_spec.rb +++ b/spec/docker/util_spec.rb @@ -99,6 +99,31 @@ expect(files_in_tar(tar)).to eq ['.dockerignore', 'baz'] end + it 'does not ignore files starting with !' do + File.write("#{tmpdir}/Dockerfile", 'bar') + + File.write("#{tmpdir}/.dockerignore", '*') + + expect(files_in_tar(tar)).to eq ['.dockerignore', 'Dockerfile'] + end + + it 'does not ignore the Dockerfile' do + File.write("#{tmpdir}/Dockerfile", 'bar') + + File.write("#{tmpdir}/.dockerignore", '*') + + expect(files_in_tar(tar)).to eq ['.dockerignore', 'Dockerfile'] + end + + it 'ignores files' do + File.write("#{tmpdir}/foo", 'bar') + File.write("#{tmpdir}/baz", 'bar') + + File.write("#{tmpdir}/.dockerignore", "foo") + + expect(files_in_tar(tar)).to eq ['.dockerignore', 'baz'] + end + it 'ignores folders' do FileUtils.mkdir("#{tmpdir}/foo") File.write("#{tmpdir}/foo/bar", 'bar') @@ -249,6 +274,61 @@ end end + describe '.ignored_files' do + attr_accessor :tmpdir + + subject do + described_class.ignored_files(tmpdir, ignore_file).map do |f| + f[tmpdir.length+1..-1] + end + end + + let(:ignore_file) { "#{tmpdir}/.dockerignore" } + + around do |example| + Dir.mktmpdir do |tmpdir| + self.tmpdir = tmpdir + example.call + end + end + + it 'ignores explicit files' do + File.write("#{tmpdir}/foo", 'bar') + File.write("#{tmpdir}/baz", 'bar') + + File.write("#{tmpdir}/.dockerignore", "foo") + + expect(subject).to eq %w(foo) + end + + it 'makes an exception to exclusions' do + File.write("#{tmpdir}/foo", 'bar') + File.write("#{tmpdir}/baz", 'bar') + + File.write("#{tmpdir}/.dockerignore", "foo\n!foo") + + expect(subject).to be_empty + end + + it 'makes an exception to exclusions (order doesnt matter)' do + File.write("#{tmpdir}/foo", 'bar') + File.write("#{tmpdir}/baz", 'bar') + + File.write("#{tmpdir}/.dockerignore", "!foo\nfoo") + + expect(subject).to be_empty + end + + it 'does not ignore the Dockerfile or .dockerignore' do + File.write("#{tmpdir}/Dockerfile", 'bar') + File.write("#{tmpdir}/baz", 'bar') + + File.write("#{tmpdir}/.dockerignore", '*') + + expect(subject).to eq %w(baz) + end + end + def files_in_tar(tar) Gem::Package::TarReader.new(tar) { |content| return content.map(&:full_name).sort } end