vagrant-reflect 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10bf068f0fcd1751185c72edc2b8a9d0f9d3fc62
4
- data.tar.gz: 62751eb5164e9a8f7bfd1f9d8793d245a300de67
3
+ metadata.gz: a125c9d631f0aa19172f28895bacb5a8e243c372
4
+ data.tar.gz: 124396c75e4bb6d37cf44ab64426dd9a496517b0
5
5
  SHA512:
6
- metadata.gz: f5639afa42e9dc1767a9a29978dbcbed82904fa70b079d12d3c72ee4bec3c93751ea1422ad8838911383426a14d7e56bfb0a1b241af353340f9603fd29366c91
7
- data.tar.gz: 2fe41628e246d06af901be938cded207f5a635a627cf1844f2d3ffdbeeda2f47f8319f9898c9d0170734eed42e700217254e98b049d3ee78bcfbe3965e1e7ae5
6
+ metadata.gz: 4a90cb48ce6ef31359cb746cca2930ffeaa3a8a2844c8cc4b662a57efe1bab7900cb0871cd59654996530e231b9c42f13305226351bcdd0586936b2ffe09dab6
7
+ data.tar.gz: ca55fd199a30b59757018e2f502ba70b77568558cd76e44439905d596f8ace5619fee25d3e9d7c0522fc33ab5e0ec2bee6aa86a04f68dd4546e4298421a1bf78
@@ -120,16 +120,11 @@ module VagrantReflect
120
120
 
121
121
  ignores = []
122
122
  opts.each do |path_opts|
123
+ ignores += path_opts[:syncer].excludes_to_regexp
123
124
  path_opts[:machine].ui.info(
124
125
  I18n.t(
125
126
  'vagrant.plugins.vagrant-reflect.rsync_auto_path',
126
127
  path: path.to_s))
127
-
128
- next unless path_opts[:opts][:exclude]
129
-
130
- Array(path_opts[:opts][:exclude]).each do |pattern|
131
- ignores << Syncer.exclude_to_regexp(pattern.to_s)
132
- end
133
128
  end
134
129
 
135
130
  @logger.info("Listening to path: #{path}")
@@ -170,43 +165,13 @@ module VagrantReflect
170
165
  @logger.info(" - Added: #{added.inspect}")
171
166
  @logger.info(" - Removed: #{removed.inspect}")
172
167
 
168
+ callback = options[:incremental] ? :sync_incremental : :sync_full
169
+
173
170
  # Perform the sync for each machine
174
171
  opts.each do |path_opts|
175
172
  begin
176
- # If we have any removals or have disabled incremental, perform a
177
- # single full sync
178
- # It's seemingly impossible to ask rsync to only do a deletion
179
- if !options[:incremental] || !removed.empty?
180
- removed.each do |remove|
181
- path_opts[:machine].ui.info(
182
- I18n.t(
183
- 'vagrant.plugins.vagrant-reflect.rsync_auto_full_remove',
184
- path: strip_path(path, remove)))
185
- end
186
-
187
- [modified, added].each do |changes|
188
- changes.each do |change|
189
- path_opts[:machine].ui.info(
190
- I18n.t(
191
- 'vagrant.plugins.vagrant-reflect.rsync_auto_full_change',
192
- path: strip_path(path, change)))
193
- end
194
- end
195
-
196
- path_opts[:machine].ui.info(
197
- I18n.t('vagrant.plugins.vagrant-reflect.rsync_auto_full'))
198
-
199
- path_opts[:syncer].sync_full
200
- elsif !modified.empty? || !added.empty?
201
- # Pass the list of changes to rsync so we quickly synchronise only
202
- # the changed files instead of the whole folder
203
- items = strip_paths(path, modified + added)
204
- path_opts[:syncer].sync_incremental(items) do |item|
205
- path_opts[:machine].ui.info(
206
- I18n.t('vagrant.plugins.vagrant-reflect.rsync_auto_increment',
207
- path: item))
208
- end
209
- end
173
+ # If disabled incremental, do a full
174
+ send callback, path, path_opts, modified, added, removed
210
175
 
211
176
  path_opts[:machine].ui.info(
212
177
  I18n.t('vagrant.plugins.vagrant-reflect.rsync_auto_synced'))
@@ -222,6 +187,50 @@ module VagrantReflect
222
187
  end
223
188
  end
224
189
 
190
+ def sync_full(path, path_opts, modified, added, removed)
191
+ [modified, added].flatten.each do |change|
192
+ path_opts[:machine].ui.info(
193
+ I18n.t(
194
+ 'vagrant.plugins.vagrant-reflect.rsync_auto_full_change',
195
+ path: strip_path(path, change)))
196
+ end
197
+
198
+ removed.each do |remove|
199
+ path_opts[:machine].ui.info(
200
+ I18n.t(
201
+ 'vagrant.plugins.vagrant-reflect.rsync_auto_full_remove',
202
+ path: strip_path(path, remove)))
203
+ end
204
+
205
+ path_opts[:machine].ui.info(
206
+ I18n.t('vagrant.plugins.vagrant-reflect.rsync_auto_full'))
207
+
208
+ path_opts[:syncer].sync_full
209
+ end
210
+
211
+ def sync_incremental(path, path_opts, modified, added, removed)
212
+ if !modified.empty? || !added.empty?
213
+ # Pass the list of changes to rsync so we quickly synchronise only
214
+ # the changed files instead of the whole folder
215
+ items = strip_paths(path, modified + added)
216
+ path_opts[:syncer].sync_incremental(items) do |item|
217
+ path_opts[:machine].ui.info(
218
+ I18n.t('vagrant.plugins.vagrant-reflect.rsync_auto_increment_change',
219
+ path: item))
220
+ end
221
+ end
222
+
223
+ return if removed.empty?
224
+
225
+ # Pass list of changes to a remove command
226
+ items = strip_paths(path, removed)
227
+ path_opts[:syncer].sync_removals(items) do |item|
228
+ path_opts[:machine].ui.info(
229
+ I18n.t('vagrant.plugins.vagrant-reflect.rsync_auto_increment_remove',
230
+ path: item))
231
+ end
232
+ end
233
+
225
234
  def strip_paths(path, items)
226
235
  items.map do |item|
227
236
  item[path.length..-1]
@@ -6,51 +6,79 @@ module VagrantReflect
6
6
  # This is a helper that abstracts out the functionality of rsyncing
7
7
  # folders so that it can be called from anywhere.
8
8
  class Syncer
9
- # This converts an rsync exclude pattern to a regular expression
10
- # we can send to Listen.
11
- def self.exclude_to_regexp(exclude)
12
- start_anchor = false
13
- dir_only = false
9
+ RSYNC_TO_REGEXP_PATTERNS = [
10
+ ['.', '\\.'],
11
+ ['***', '|||EMPTY|||'],
12
+ ['**', '|||GLOBAL|||'],
13
+ ['*', '|||PATH|||'],
14
+ ['?', '[^/]'],
15
+ ['|||PATH|||', '[^/]+'],
16
+ ['|||GLOBAL|||', '.+'],
17
+ ['|||EMPTY|||', '.*']
18
+ ].freeze
14
19
 
15
- if exclude.start_with?('/')
16
- start_anchor = true
17
- exclude = exclude[1..-1]
18
- end
20
+ def initialize(machine, opts)
21
+ @opts = opts
22
+ @machine = machine
23
+ @workdir = @machine.env.root_path.to_s
19
24
 
20
- if exclude.end_with?('/')
21
- dir_only = true
22
- exclude = exclude[0..-2]
23
- end
25
+ init_paths
26
+ init_connection_info
27
+ init_excludes
28
+ init_commands
29
+ end
24
30
 
25
- regexp = start_anchor ? '^' : '(?:^|/)'
31
+ def log_configuration
32
+ @machine.ui.info(
33
+ I18n.t(
34
+ 'vagrant.plugins.vagrant-reflect.rsync_folder_configuration',
35
+ guestpath: @guestpath,
36
+ hostpath: @hostpath))
26
37
 
27
- # This is REALLY ghetto, but its a start. We can improve and
28
- # keep unit tests passing in the future.
29
- # TODO: Escaped wildcards get substituted incorrectly - replace with FSM?
30
- exclude = exclude.gsub('.', '\\.')
31
- exclude = exclude.gsub('***', '|||EMPTY|||')
32
- exclude = exclude.gsub('**', '|||GLOBAL|||')
33
- exclude = exclude.gsub('*', '|||PATH|||')
34
- exclude = exclude.gsub('?', '[^/]')
35
- exclude = exclude.gsub('|||PATH|||', '[^/]+')
36
- exclude = exclude.gsub('|||GLOBAL|||', '.+')
37
- exclude = exclude.gsub('|||EMPTY|||', '.*')
38
- regexp += exclude
39
-
40
- regexp += dir_only ? '/' : '(?:/|$)'
38
+ return if @excludes.empty?
41
39
 
42
- Regexp.new(regexp)
40
+ @machine.ui.info(
41
+ I18n.t(
42
+ 'vagrant.plugins.vagrant-reflect.rsync_folder_excludes',
43
+ excludes: @excludes.inspect))
43
44
  end
44
45
 
45
- def initialize(machine, opts)
46
- @opts = opts
47
- @machine = machine
48
- @workdir = @machine.env.root_path.to_s
46
+ # This converts the rsync exclude patterns to regular expressions we can
47
+ # send to Listen.
48
+ def excludes_to_regexp
49
+ return @regexp if @regexp
50
+
51
+ @regexp = @excludes.map(&method(:exclude_to_regex_single))
52
+ end
53
+
54
+ def sync_incremental(items, &block)
55
+ send_items_to_command items, @rsync_command_inc, &block
56
+ end
57
+
58
+ def sync_full
59
+ r = Vagrant::Util::SubprocessPatched.execute(*@rsync_command_full)
60
+ check_exit @rsync_command_full, r
61
+ end
62
+
63
+ def sync_removals(items, &block)
64
+ # Look for removed directories and fill in guest paths
65
+ dirs = prepare_items_for_removal(items)
66
+
67
+ send_items_to_command items, @rm_command, &block
68
+ sync_removals_parents dirs.values, &block unless dirs.empty?
69
+ end
49
70
 
71
+ def sync_removals_parents(guest_items, &block)
72
+ send_items_to_command guest_items, @rmdir_command, &block
73
+ end
74
+
75
+ protected
76
+
77
+ def init_paths
50
78
  # Folder info
51
79
  @guestpath = @opts[:guestpath]
52
80
  @hostpath = @opts[:hostpath]
53
- @hostpath = File.expand_path(@hostpath, machine.env.root_path)
81
+ @hostpath = File.expand_path(@hostpath, @machine.env.root_path)
54
82
  @hostpath = Vagrant::Util::Platform.fs_real_path(@hostpath).to_s
55
83
 
56
84
  if Vagrant::Util::Platform.windows?
@@ -61,86 +89,113 @@ module VagrantReflect
61
89
  # Make sure the host path ends with a "/" to avoid creating
62
90
  # a nested directory...
63
91
  @hostpath += '/' unless @hostpath.end_with?('/')
92
+ end
64
93
 
94
+ def init_connection_info
65
95
  # Connection information
66
96
  username = @machine.ssh_info[:username]
67
97
  host = @machine.ssh_info[:host]
68
- proxy_command = ''
98
+ @remote = "#{username}@#{host}"
99
+ @target = "#{@remote}:#{@guestpath}"
100
+ end
101
+
102
+ def init_excludes
103
+ # Exclude some files by default, and any that might be configured
104
+ # by the user.
105
+ @excludes = []
106
+ @excludes += Array(@opts[:exclude]).map(&:to_s) if @opts[:exclude]
107
+ @excludes.uniq!
108
+ end
109
+
110
+ def init_commands
111
+ init_rsh_command
112
+ init_rsync_command
113
+ init_rsync_command_full
114
+ init_rsync_command_inc
115
+ init_rm_command
116
+ init_rmdir_command
117
+ end
118
+
119
+ def init_rsh_command
120
+ proxy_command = []
69
121
  if @machine.ssh_info[:proxy_command]
70
- proxy_command = "-o ProxyCommand='#{@machine.ssh_info[:proxy_command]}' "
122
+ proxy_command += [
123
+ '-o',
124
+ "ProxyCommand='#{@machine.ssh_info[:proxy_command]}'"
125
+ ]
71
126
  end
72
127
 
73
- rsh = [
74
- "ssh -p #{@machine.ssh_info[:port]} " +
75
- proxy_command +
76
- '-o StrictHostKeyChecking=no '\
77
- '-o IdentitiesOnly=true '\
78
- '-o UserKnownHostsFile=/dev/null',
79
- @machine.ssh_info[:private_key_path].map { |p| "-i '#{p}'" }
80
- ].flatten.join(' ')
81
-
82
- @target = "#{username}@#{host}:#{@guestpath}"
128
+ @rsh = build_rsh_command(proxy_command).flatten
129
+ end
83
130
 
84
- # Exclude some files by default, and any that might be configured
85
- # by the user.
86
- excludes = []
87
- excludes += Array(@opts[:exclude]).map(&:to_s) if @opts[:exclude]
88
- excludes.uniq!
131
+ def build_rsh_command(proxy_command)
132
+ [
133
+ 'ssh',
134
+ '-p', @machine.ssh_info[:port].to_s,
135
+ proxy_command,
136
+ '-o', 'StrictHostKeyChecking=no',
137
+ '-o', 'IdentitiesOnly=true',
138
+ '-o', 'UserKnownHostsFile=/dev/null',
139
+ @machine.ssh_info[:private_key_path].map { |p| ['-i', p] }
140
+ ]
141
+ end
89
142
 
143
+ def init_rsync_command
90
144
  # Get the command-line arguments
91
- @command = ['rsync']
92
- @command += Array(@opts[:args]).dup if @opts[:args]
93
- @command ||= ['--verbose', '--archive', '--delete', '-z', '--copy-links']
145
+ @rsync_command = ['rsync']
146
+ @rsync_command += Array(@opts[:args]).dup if @opts[:args]
147
+ @rsync_command ||= [
148
+ '--verbose', '--archive', '--delete', '-z', '--copy-links']
94
149
 
95
150
  # On Windows, we have to set a default chmod flag to avoid permission
96
151
  # issues
97
- if Vagrant::Util::Platform.windows?
98
- unless @command.any? { |arg| arg.start_with?('--chmod=') }
99
- # Ensures that all non-masked bits get enabled
100
- @command << '--chmod=ugo=rwX'
101
-
102
- # Remove the -p option if --archive is enabled (--archive equals
103
- # -rlptgoD) otherwise new files will not have the destination-default
104
- # permissions
105
- @command << '--no-perms' if
106
- @command.include?('--archive') || @command.include?('-a')
107
- end
108
- end
152
+ build_windows_chmod_args if Vagrant::Util::Platform.windows?
153
+
154
+ build_owner_args
155
+
156
+ @rsync_command << '-e'
157
+ @rsync_command << @rsh.join(' ')
158
+
159
+ @excludes.map { |e| @rsync_command += ['--exclude', e] }
160
+ end
161
+
162
+ def build_windows_chmod_args
163
+ return if @rsync_command.any? { |arg| arg.start_with?('--chmod=') }
109
164
 
165
+ # Ensures that all non-masked bits get enabled
166
+ @rsync_command << '--chmod=ugo=rwX'
167
+
168
+ # Remove the -p option if --archive is enabled (--archive equals
169
+ # -rlptgoD) otherwise new files will not have the destination-default
170
+ # permissions
171
+ return unless @rsync_command.include?('--archive') ||
172
+ @rsync_command.include?('-a')
173
+
174
+ @rsync_command << '--no-perms'
175
+ end
176
+
177
+ def build_owner_args
110
178
  # Disable rsync's owner/group preservation (implied by --archive) unless
111
179
  # specifically requested, since we adjust owner/group to match shared
112
180
  # folder setting ourselves.
113
- @command << '--no-owner' unless
114
- @command.include?('--owner') || @command.include?('-o')
115
- @command << '--no-group' unless
116
- @command.include?('--group') || @command.include?('-g')
117
-
118
- # Tell local rsync how to invoke remote rsync with sudo
119
- if @machine.guest.capability?(:rsync_command)
120
- @command << '--rsync-path' << @machine.guest.capability(:rsync_command)
121
- end
181
+ @rsync_command << '--no-owner' unless
182
+ @rsync_command.include?('--owner') || @rsync_command.include?('-o')
183
+ @rsync_command << '--no-group' unless
184
+ @rsync_command.include?('--group') || @rsync_command.include?('-g')
185
+ end
122
186
 
123
- @command += [
124
- '-e', rsh
187
+ def init_rsync_command_full
188
+ @rsync_command_full = @rsync_command.dup + [
189
+ @hostpath,
190
+ @target,
191
+ {
192
+ workdir: @workdir
193
+ }
125
194
  ]
126
-
127
- excludes.map { |e| @command += ['--exclude', e] }
128
-
129
- machine.ui.info(
130
- I18n.t(
131
- 'vagrant.plugins.vagrant-reflect.rsync_folder_configuration',
132
- guestpath: @guestpath,
133
- hostpath: @hostpath))
134
- if excludes.length > 1
135
- machine.ui.info(
136
- I18n.t(
137
- 'vagrant.plugins.vagrant-reflect.rsync_folder_excludes',
138
- excludes: excludes.inspect))
139
- end
140
195
  end
141
196
 
142
- def sync_incremental(items, &block)
143
- command = @command.dup + [
197
+ def init_rsync_command_inc
198
+ @rsync_command_inc = @rsync_command.dup + [
144
199
  '--files-from=-',
145
200
  @hostpath,
146
201
  @target,
@@ -149,56 +204,86 @@ module VagrantReflect
149
204
  notify: :stdin
150
205
  }
151
206
  ]
152
-
153
- current = false
154
- r = Vagrant::Util::SubprocessPatched.execute(*command) do |what, io|
155
- next if what != :stdin
156
-
157
- current = process_items(io, items, current, &block)
158
- end
159
-
160
- check_exit command, r
161
207
  end
162
208
 
163
- def sync_full
164
- command = @command.dup + [
165
- @hostpath,
166
- @target,
209
+ def init_rm_command
210
+ @rm_command = @rsh.dup + [
211
+ @remote,
212
+ 'xargs rm -f',
167
213
  {
168
- workdir: @workdir
214
+ workdir: @workdir,
215
+ notify: :stdin
169
216
  }
170
217
  ]
218
+ end
171
219
 
172
- r = Vagrant::Util::SubprocessPatched.execute(*command)
173
-
174
- check_exit command, r
220
+ def init_rmdir_command
221
+ @rmdir_command = @rsh.dup + [
222
+ @remote,
223
+ 'xargs rmdir',
224
+ {
225
+ workdir: @workdir,
226
+ notify: :stdin
227
+ }
228
+ ]
175
229
  end
176
230
 
177
- protected
231
+ def prepare_items_for_removal(items)
232
+ dirs = {}
233
+ items.map! do |rel_path|
234
+ check_for_empty_parents(rel_path, dirs)
235
+ @guestpath + rel_path
236
+ end
237
+ dirs
238
+ end
178
239
 
179
- def process_items(io, items, current, &block)
180
- until items.empty?
181
- # If we don't have a line, grab one and print it
182
- if current === false
183
- current = items.shift + "\n"
184
- block.call(current) if block_given?
240
+ def check_for_empty_parents(rel_path, dirs)
241
+ parent = rel_path
242
+ loop do
243
+ parent = File.dirname(parent)
244
+ break if parent == '/'
245
+ unless File.exist?(@hostpath + parent) || dirs.key?(parent)
246
+ dirs[parent] = @guestpath + parent
185
247
  end
248
+ end
249
+ end
186
250
 
187
- # Handle partial writes
188
- n = io.write_nonblock(current)
189
- if n < current.length
190
- current = current[n..current.length]
191
- break
192
- end
251
+ def send_items_to_command(items, command, &block)
252
+ current = next_item(items, &block)
253
+ r = Vagrant::Util::SubprocessPatched.execute(*command) do |what, io|
254
+ next if what != :stdin
193
255
 
194
- # Request a new line for next write
195
- current = false
256
+ current = process_items(io, items, current, &block)
257
+ end
258
+ check_exit command, r
259
+ end
260
+
261
+ def process_items(io, items, current, &block)
262
+ until current.nil?
263
+ send_data(io, current)
264
+ current = next_item(items, &block)
196
265
  end
197
266
 
198
267
  # Finished! Close stdin
199
268
  io.close_write
200
269
  rescue IO::WaitWritable, Errno::EINTR
201
- # Ignore
270
+ # Wait for writable again
271
+ return current
272
+ end
273
+
274
+ def next_item(items)
275
+ return nil if items.empty?
276
+ current = items.shift + "\n"
277
+ yield current if block_given?
278
+ current
279
+ end
280
+
281
+ def send_data(io, current)
282
+ # Handle partial writes
283
+ n = io.write_nonblock(current)
284
+ return unless n < current.length
285
+ current.slice! 0, n
286
+ throw IO::WaitWritable
202
287
  end
203
288
 
204
289
  def check_exit(command, r)
@@ -210,5 +295,30 @@ module VagrantReflect
210
295
  hostpath: @hostpath,
211
296
  stderr: r.stderr
212
297
  end
298
+
299
+ def exclude_to_regex_single(exclude)
300
+ start_anchor = false
301
+
302
+ if exclude.start_with?('/')
303
+ start_anchor = true
304
+ exclude = exclude[1..-1]
305
+ end
306
+
307
+ regexp = start_anchor ? '^' : '(?:^|/)'
308
+ regexp += perform_substitutions(exclude)
309
+ regexp += exclude.end_with?('/') ? '' : '(?:/|$)'
310
+
311
+ Regexp.new(regexp)
312
+ end
313
+
314
+ def perform_substitutions(exclude)
315
+ # This is REALLY ghetto, but its a start. We can improve and
316
+ # keep unit tests passing in the future.
317
+ # TODO: Escaped wildcards get substituted incorrectly - replace with FSM?
318
+ RSYNC_TO_REGEXP_PATTERNS.each do |pattern|
319
+ exclude = exclude.gsub(pattern[0], pattern[1])
320
+ end
321
+ exclude
322
+ end
213
323
  end
214
324
  end
@@ -1,3 +1,3 @@
1
1
  module VagrantReflect
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -14,8 +14,10 @@ en:
14
14
  have specified `rsync_auto` to be false.
15
15
  rsync_auto_path: |-
16
16
  Watching: %{path}
17
- rsync_auto_increment: |-
17
+ rsync_auto_increment_change: |-
18
18
  Sending change: %{path}
19
+ rsync_auto_increment_remove: |-
20
+ Sending removal: %{path}
19
21
  rsync_auto_full_change: |-
20
22
  Processing change: %{path}
21
23
  rsync_auto_full_remove: |-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-reflect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Woods
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-23 00:00:00.000000000 Z
11
+ date: 2016-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: driskell-listen