diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b461b279..96bbc3ec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ IMPROVEMENTS: - guests/tinycore: Will now auto-install rsync. + - synced\_folders/rsync: rsync-auto will not watch filesystem for + excluded paths. [GH-3159] BUG FIXES: diff --git a/plugins/synced_folders/rsync/command/rsync_auto.rb b/plugins/synced_folders/rsync/command/rsync_auto.rb index 1f6627198..8e1951214 100644 --- a/plugins/synced_folders/rsync/command/rsync_auto.rb +++ b/plugins/synced_folders/rsync/command/rsync_auto.rb @@ -31,6 +31,7 @@ module VagrantPlugins # Build up the paths that we need to listen to. paths = {} + ignores = [] with_target_vms(argv) do |machine| folders = synced_folders(machine)[:rsync] next if !folders || folders.empty? @@ -48,6 +49,12 @@ module VagrantPlugins machine: machine, opts: folder_opts, } + + if folder_opts[:exclude] + Array(folder_opts[:exclude]).each do |pattern| + ignores << RsyncHelper.exclude_to_regexp(hostpath, pattern.to_s) + end + end end end @@ -62,9 +69,13 @@ module VagrantPlugins end @logger.info("Listening to paths: #{paths.keys.sort.inspect}") + @logger.info("Ignoring #{ignores.length} paths:") + ignores.each do |ignore| + @logger.info(" -- #{ignore.to_s}") + end @logger.info("Listening via: #{Listen::Adapter.select.inspect}") callback = method(:callback).to_proc.curry[paths] - listener = Listen.to(*paths.keys, &callback) + listener = Listen.to(*paths.keys, ignore: ignores, &callback) listener.start listener.thread.join @@ -73,10 +84,10 @@ module VagrantPlugins # This is the callback that is called when any changes happen def callback(paths, modified, added, removed) - @logger.debug("File change callback called!") - @logger.debug(" - Modified: #{modified.inspect}") - @logger.debug(" - Added: #{added.inspect}") - @logger.debug(" - Removed: #{removed.inspect}") + @logger.info("File change callback called!") + @logger.info(" - Modified: #{modified.inspect}") + @logger.info(" - Added: #{added.inspect}") + @logger.info(" - Removed: #{removed.inspect}") tosync = [] paths.each do |hostpath, folders| diff --git a/plugins/synced_folders/rsync/helper.rb b/plugins/synced_folders/rsync/helper.rb index 503f05e82..f5c081a74 100644 --- a/plugins/synced_folders/rsync/helper.rb +++ b/plugins/synced_folders/rsync/helper.rb @@ -6,6 +6,31 @@ module VagrantPlugins # This is a helper that abstracts out the functionality of rsyncing # folders so that it can be called from anywhere. class RsyncHelper + # This converts an rsync exclude pattern to a regular expression + # we can send to Listen. + def self.exclude_to_regexp(path, exclude) + start_anchor = false + + if exclude.start_with?("/") + start_anchor = true + exclude = exclude[1..-1] + end + + path = "#{path}/" if !path.end_with?("/") + regexp = "^#{Regexp.escape(path)}" + regexp += ".*" if !start_anchor + + # This is REALLY ghetto, but its a start. We can improve and + # keep unit tests passing in the future. + exclude = exclude.gsub("**", "|||GLOBAL|||") + exclude = exclude.gsub("*", "|||PATH|||") + exclude = exclude.gsub("|||PATH|||", "[^/]*") + exclude = exclude.gsub("|||GLOBAL|||", ".*") + regexp += exclude + + Regexp.new(regexp) + end + def self.rsync_single(machine, ssh_info, opts) # Folder info guestpath = opts[:guestpath] diff --git a/test/unit/plugins/synced_folders/rsync/helper_test.rb b/test/unit/plugins/synced_folders/rsync/helper_test.rb index b416b4902..7b2d7561e 100644 --- a/test/unit/plugins/synced_folders/rsync/helper_test.rb +++ b/test/unit/plugins/synced_folders/rsync/helper_test.rb @@ -28,6 +28,30 @@ describe VagrantPlugins::SyncedFolderRSync::RsyncHelper do end end + describe "#exclude_to_regexp" do + let(:path) { "/foo/bar" } + + it "converts a directory match" do + expect(described_class.exclude_to_regexp(path, "foo/")). + to eq(/^#{Regexp.escape(path)}\/.*foo\//) + end + + it "converts the start anchor" do + expect(described_class.exclude_to_regexp(path, "/foo")). + to eq(/^\/foo\/bar\/foo/) + end + + it "converts the **" do + expect(described_class.exclude_to_regexp(path, "fo**o")). + to eq(/^#{Regexp.escape(path)}\/.*fo.*o/) + end + + it "converts the *" do + expect(described_class.exclude_to_regexp(path, "fo*o")). + to eq(/^#{Regexp.escape(path)}\/.*fo[^\/]*o/) + end + end + describe "#rsync_single" do let(:result) { Vagrant::Util::Subprocess::Result.new(0, "", "") }