diff --git a/lib/docker/util.rb b/lib/docker/util.rb index 2ca8dc6e1..6096028f5 100644 --- a/lib/docker/util.rb +++ b/lib/docker/util.rb @@ -260,7 +260,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) @@ -269,11 +269,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 a0a3cc479..22ba02bae 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