vagrant-reflect 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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