vagrant-mirror 0.1.1.alpha → 0.1.2.alpha

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.
data/Gemfile.lock CHANGED
@@ -14,7 +14,6 @@ GEM
14
14
  coderay (1.0.8)
15
15
  diff-lcs (1.1.3)
16
16
  erubis (2.7.0)
17
- ffi (1.2.0)
18
17
  ffi (1.2.0-x86-mingw32)
19
18
  guard (1.6.1)
20
19
  listen (>= 0.6.0)
@@ -33,10 +32,6 @@ GEM
33
32
  net-scp (1.0.4)
34
33
  net-ssh (>= 1.99.1)
35
34
  net-ssh (2.2.2)
36
- pry (0.9.10)
37
- coderay (~> 1.0.5)
38
- method_source (~> 0.8)
39
- slop (~> 3.3.1)
40
35
  pry (0.9.10-x86-mingw32)
41
36
  coderay (~> 1.0.5)
42
37
  method_source (~> 0.8)
@@ -18,12 +18,20 @@ module Vagrant
18
18
  # Makes a blocking call to Guard to listen on the configured path
19
19
  def listen!
20
20
  Listen.to(@path, :relative_paths => true) do | modified, added, removed |
21
- @queue << {
22
- :source => :host,
23
- :added => added,
24
- :modified => modified,
25
- :removed => removed
26
- }
21
+
22
+ # Add each reported file to the queue
23
+ modified.each do | path |
24
+ @queue << { :event => :modified, :path => path }
25
+ end
26
+
27
+ added.each do | path |
28
+ @queue << { :event => :added, :path => path }
29
+ end
30
+
31
+ removed.each do | path |
32
+ @queue << { :event => :removed, :path => path }
33
+ end
34
+
27
35
  end
28
36
  end
29
37
 
@@ -15,18 +15,21 @@ module Vagrant
15
15
  @env = env
16
16
  end
17
17
 
18
- # Executes the middleware and then continues to the next middleware in the
19
- # stack
18
+ # Loads the rest of the middlewares first, then finishes up by running
19
+ # our middleware. This is required because the core share_folders and
20
+ # provision middlewares don not mount the shares until the very end of
21
+ # the process and we need to run after that
20
22
  #
21
23
  # @param [Vagrant::Action::Environment] The environment
22
24
  def call(env)
25
+ @app.call(env)
26
+
23
27
  mirrors = env[:vm].config.mirror.folders
24
28
  if !mirrors.empty?
25
29
  execute(mirrors, env)
26
30
  else
27
31
  env[:ui].info("No vagrant-mirror mirrored folders configured for this box")
28
32
  end
29
- @app.call(env)
30
33
  end
31
34
 
32
35
  protected
@@ -8,22 +8,6 @@ module Vagrant
8
8
  module Middleware
9
9
  class Mirror < Base
10
10
 
11
- # Loads the rest of the middlewares first, then finishes up by running
12
- # the mirror middleware. This allows the listener to start after the
13
- # instance has been provisioned.
14
- #
15
- # @param [Vagrant::Action::Environment] The environment
16
- def call(env)
17
- @app.call(env)
18
-
19
- mirrors = env[:vm].config.mirror.folders
20
- if !mirrors.empty?
21
- execute(mirrors, env)
22
- else
23
- env[:ui].info("No vagrant-mirror mirrored folders configured for this box")
24
- end
25
- end
26
-
27
11
  protected
28
12
 
29
13
  # Mirrors the folder pairs configured in the vagrantfile
@@ -31,6 +15,7 @@ module Vagrant
31
15
  # @param [Array] The folder pairs to synchronise
32
16
  # @param [Vagrant::Action::Environment] The environment
33
17
  def execute(mirrors, env)
18
+
34
19
  ui = env[:ui]
35
20
  ui.info("Beginning directory mirroring")
36
21
 
@@ -55,26 +40,34 @@ module Vagrant
55
40
  if (change[:quit])
56
41
  quit = true
57
42
  else
43
+ # Ignore files that match the configured exclude paths
44
+ if exclude?(change[:path], mirror_config)
45
+ next
46
+ end
47
+
58
48
  # Handle removed files first - guard sometimes flagged as deleted when they aren't
59
49
  # So we first check if the file has been deleted on the host. If so, we delete on
60
50
  # the guest, otherwise we add to the list to rsync in case there are changes
61
- changes = []
62
- change[:removed].each do | relpath |
63
- if (File.exists?(File.join(host_path, relpath)))
64
- changes << relpath
65
- else
66
- target = "#{mirror_config[:guest_path]}/#{relpath}"
51
+ if (change[:event] == :removed)
52
+ unless File.exists?(File.join(host_path, change[:path]))
53
+ # Delete the file on the guest
54
+ target = "#{mirror_config[:guest_path]}/#{change[:path]}"
67
55
  ui.warn("XX Deleting #{target}")
68
56
  env[:vm].channel.sudo("rm #{target}")
57
+
58
+ # Beep if configured
59
+ if (mirror_config[:beep])
60
+ print "\a"
61
+ end
62
+
63
+ # Move to the next file
64
+ next
69
65
  end
70
66
  end
71
67
 
72
- # Add to the list of deletions with each of the modified and added files
73
- changes = changes + change[:added] + change[:modified]
74
- changes.each do | relpath |
75
- ui.info(">> #{relpath}")
76
- rsync.run(relpath)
77
- end
68
+ # Otherwise, run rsync on the file
69
+ ui.info(">> #{change[:path]}")
70
+ rsync.run(change[:path])
78
71
 
79
72
  # Beep if configured
80
73
  if (mirror_config[:beep])
@@ -102,6 +95,71 @@ module Vagrant
102
95
  ui.success("Completed directory synchronisation")
103
96
  end
104
97
 
98
+ # Checks whether a given path should be excluded based on the :exclude config for this mirror
99
+ #
100
+ # @param [String] The file path being processed
101
+ # @param [Hash] The mirror config options
102
+ #
103
+ # @return [Bool] Whether to exclude this file
104
+ def exclude?(path, mirror_config)
105
+ compiled_excludes = mirror_config.fetch(:compiled_excludes, {})
106
+ excluded = false
107
+
108
+ mirror_config[:exclude].each do | exclude |
109
+ # Check if it has been compiled
110
+ unless compiled_excludes.has_key? exclude
111
+ compiled_excludes[exclude] = compile_exclude(exclude)
112
+ end
113
+ exclude = compiled_excludes[exclude]
114
+
115
+ # Test for a match against the path
116
+ if exclude.match(path)
117
+ excluded = true
118
+ break
119
+ end
120
+ end
121
+
122
+ # Return the result
123
+ excluded
124
+ end
125
+
126
+ # Mirrors the folder pairs configured in the vagrantfile
127
+ #
128
+ # @param [String] A glob-style exclude format
129
+ #
130
+ # @return [Regexp] The exclude path as a regex
131
+ def compile_exclude(exclude)
132
+ exclude = exclude.dup
133
+
134
+ # Absolute path is tied to start of string, relative to any directory separator
135
+ if exclude.chars.first == '/'
136
+ regex = "^"
137
+ exclude[0] = ''
138
+ else
139
+ regex = "(^|/)"
140
+ end
141
+
142
+ # Temporarily convert wildcards to placeholders
143
+ exclude.sub!('**','<<globwild2>>')
144
+ exclude.sub!('*','<<globwild>>')
145
+
146
+ # Escape the string for regexp characters
147
+ exclude = Regexp.escape(exclude)
148
+
149
+
150
+ # one star matches anything except directories
151
+ exclude.sub!('<<globwild>>', '[^/]*?')
152
+
153
+ # two stars match anything including directories
154
+ exclude.sub!('<<globwild2>>','.*?')
155
+
156
+ regex << exclude
157
+
158
+ # pattern should always end on a directory separator or end of string
159
+ regex << '($|/)'
160
+
161
+ Regexp.new(regex)
162
+ end
105
163
  end
106
164
  end
107
165
  end
@@ -10,23 +10,6 @@ module Vagrant
10
10
  module Middleware
11
11
  class Sync < Base
12
12
 
13
- # Loads the rest of the middlewares first, then finishes up by running
14
- # the sync middleware. This is required because the core share_folders
15
- # middleware does not mount the shares until the very end of the process
16
- # and we need to run after that
17
- #
18
- # @param [Vagrant::Action::Environment] The environment
19
- def call(env)
20
- @app.call(env)
21
-
22
- mirrors = env[:vm].config.mirror.folders
23
- if !mirrors.empty?
24
- execute(mirrors, env)
25
- else
26
- env[:ui].info("No vagrant-mirror mirrored folders configured for this box")
27
- end
28
- end
29
-
30
13
  protected
31
14
 
32
15
  # Synchronizes the folder pairs configured in the vagrantfile
@@ -75,6 +58,7 @@ module Vagrant
75
58
  rsync.run('/')
76
59
  end
77
60
 
61
+
78
62
  rescue RuntimeError => e
79
63
  # Pass through Vagrant errors
80
64
  if e.is_a? Vagrant::Errors::VagrantError
@@ -1,5 +1,5 @@
1
1
  module Vagrant
2
2
  module Mirror
3
- VERSION = "0.1.1.alpha"
3
+ VERSION = "0.1.2.alpha"
4
4
  end
5
5
  end
@@ -26,40 +26,46 @@ describe Vagrant::Mirror::Listener::Host do
26
26
  context "when changes received" do
27
27
  before(:each) do
28
28
  Listen.stub(:to)
29
- .and_yield(['modified'],[],[])
30
- .and_yield([],['added'],[])
31
- .and_yield([],[],['removed'])
32
- .and_yield(['modified1'],['added1'],['removed1'])
29
+ .and_yield(['modified1','modified2'],[],[])
30
+ .and_yield([],['added1','added2'],[])
31
+ .and_yield([],[],['removed1','removed2'])
32
+ .and_yield(['modified3'],['added3'],['removed3'])
33
33
  end
34
34
 
35
- it "pushes added files onto the queue" do
35
+ it "pushes added files onto the queue one by one" do
36
36
  queue.should_receive(:"<<")
37
- .with(hash_including(:added => ['added']))
37
+ .with(hash_including({:event => :added, :path => 'added1'}))
38
+ queue.should_receive(:"<<")
39
+ .with(hash_including({:event => :added, :path => 'added2'}))
38
40
 
39
41
  subject.listen!
40
42
  end
41
43
 
42
- it "pushes modified files onto the queue" do
44
+ it "pushes modified files onto the queue one by one" do
45
+ queue.should_receive(:"<<")
46
+ .with(hash_including({:event => :modified, :path => 'modified1'}))
43
47
  queue.should_receive(:"<<")
44
- .with(hash_including(:modified => ['modified']))
48
+ .with(hash_including({:event => :modified, :path => 'modified2'}))
45
49
 
46
50
  subject.listen!
47
51
  end
48
52
 
49
- it "pushes removed files onto the queue" do
53
+ it "pushes removed files onto the queue one by one" do
54
+ queue.should_receive(:"<<")
55
+ .with(hash_including({:event => :removed, :path => 'removed1'}))
50
56
  queue.should_receive(:"<<")
51
- .with(hash_including(:removed => ['removed']))
57
+ .with(hash_including({:event => :removed, :path => 'removed2'}))
52
58
 
53
59
  subject.listen!
54
60
  end
55
61
 
56
62
  it "handles simultaneous changes" do
57
63
  queue.should_receive(:"<<")
58
- .with(hash_including({
59
- :added => ['added1'],
60
- :modified => ['modified1'],
61
- :removed => ['removed1']
62
- }))
64
+ .with(hash_including({:event => :added, :path => 'added3'}))
65
+ queue.should_receive(:"<<")
66
+ .with(hash_including({:event => :modified, :path => 'modified3'}))
67
+ queue.should_receive(:"<<")
68
+ .with(hash_including({:event => :removed, :path => 'removed3'}))
63
69
 
64
70
  subject.listen!
65
71
  end
@@ -54,9 +54,10 @@ describe Vagrant::Mirror::Middleware::Mirror do
54
54
  end
55
55
 
56
56
  context "with a mirrored folder" do
57
+ let ( :mirror_options ) { {} }
57
58
 
58
59
  before (:each) do
59
- config.mirror.vagrant_root '/var/guest'
60
+ config.mirror.vagrant_root '/var/guest', mirror_options
60
61
  config.vm.share_folder("v-root", "/vagrant", ".")
61
62
  end
62
63
 
@@ -122,50 +123,115 @@ describe Vagrant::Mirror::Middleware::Mirror do
122
123
 
123
124
  context "with notifications in the queue" do
124
125
 
125
- let (:added) { ['added'] }
126
- let (:modified) { ['modified','modified2'] }
127
- let (:removed) { ['removed'] }
128
- let (:rm_exists) { true }
126
+ let (:added) { { :event => :added, :path => 'added' } }
127
+ let (:modified) { { :event => :modified, :path => 'modified'} }
128
+ let (:removed_exists) { { :event => :removed, :path => 'removed_exists'}}
129
+ let (:removed_remvd) { { :event => :removed, :path => 'removed_remvd'}}
129
130
 
130
131
  before (:each) do
131
132
  queue.stub(:pop).and_return(
132
- { :added => added, :modified => modified, :removed => removed },
133
- { :added => added, :modified => modified, :removed => removed },
134
- { :quit => true })
135
- File.stub(:exists?).with("#{env[:root_path]}/removed").and_return(rm_exists)
133
+ added,
134
+ modified,
135
+ removed_exists,
136
+ removed_remvd,
137
+ { :quit => true }
138
+ )
139
+
140
+ File.stub(:exists?).with("#{env[:root_path]}/removed_exists").and_return(true)
141
+ File.stub(:exists?).with("#{env[:root_path]}/removed_remvd").and_return(false)
136
142
  end
137
143
 
138
144
  it "runs rsync one by one for each added and modified file" do
139
145
  rsync.should_receive(:run).with("added")
140
146
  rsync.should_receive(:run).with("modified")
141
- rsync.should_receive(:run).with("modified2")
142
147
 
143
148
  subject.call(env)
144
149
  sleep 0.2
145
150
  end
146
151
 
147
- # Sometimes Guard flags a file as deleted that still exists - during certain types of
148
- # atomic file writes, we think. So we should delete the file remotely if so, or sync if not.
149
- context "if deleted files have been deleted" do
150
- let (:rm_exists) { false }
152
+ it "deletes files by SSH if they have been deleted on the host" do
153
+ channel.should_receive(:sudo).with('rm /var/guest/removed_remvd')
154
+
155
+ subject.call(env)
156
+ sleep 0.2
157
+ end
158
+
159
+ it "runs rsync to update deleted files if they still exist on the host" do
160
+ rsync.should_receive(:run).with('removed_exists')
161
+
162
+ subject.call(env)
163
+ sleep 0.2
164
+ end
165
+ end
166
+
167
+ context "with notifications in the queue when exclude paths are configured" do
168
+ let (:mirror_options) { { :exclude => [ "/docs", "cache", "*.png", "dir*", "/vendor/**/test"] } }
169
+
170
+ # For these tests we want to know about unexpected calls
171
+ let (:rsync) { double("Vagrant::Mirror::Rsync") }
172
+
173
+ it "correctly filters absolute paths within the sync folder" do
174
+ queue.stub(:pop).and_return(
175
+ { :event => :added, :path => "docs/should/ignore.fl" },
176
+ { :event => :added, :path => "should/not/ignore/docs.txt" },
177
+ { :quit => true })
178
+
179
+ rsync.should_receive(:run).with('should/not/ignore/docs.txt')
180
+
181
+ subject.call(env)
182
+ sleep 0.2
183
+ end
184
+
185
+ it "correctly filters named directories within the sync folder" do
186
+ queue.stub(:pop).and_return(
187
+ { :event => :added, :path => "cache/should/ignore.fl" },
188
+ { :event => :added, :path => "should/cache/ignore.fl" },
189
+ { :event => :added, :path => "cacheit/should/not/ignore.fl" },
190
+ { :quit => true })
191
+
192
+ rsync.should_receive(:run).with('cacheit/should/not/ignore.fl')
193
+
194
+ subject.call(env)
195
+ sleep 0.2
196
+ end
197
+
198
+ it "correctly filters filename wildcards" do
199
+ queue.stub(:pop).and_return(
200
+ { :event => :added, :path => "ignore.png" },
201
+ { :event => :added, :path => "should/ignore.png" },
202
+ { :event => :added, :path => "should/not/png/ignore.txt" },
203
+ { :quit => true })
151
204
 
152
- it "deletes them by SSH" do
153
- channel.should_receive(:sudo).with('rm /var/guest/removed')
205
+ rsync.should_receive(:run).with('should/not/png/ignore.txt')
154
206
 
155
- subject.call(env)
156
- sleep 0.2
157
- end
207
+ subject.call(env)
208
+ sleep 0.2
158
209
  end
159
210
 
160
- context "if deleted files have not been deleted" do
161
- let (:rm_exists) { true }
211
+ it "correctly filters glob patterns with *" do
212
+ queue.stub(:pop).and_return(
213
+ { :event => :added, :path => "dir1/should/ignore.fl" },
214
+ { :event => :added, :path => "should/dirignore/this.tst" },
215
+ { :event => :added, :path => "should/notdir/ignore.tst" },
216
+ { :quit => true })
217
+
218
+ rsync.should_receive(:run).with('should/notdir/ignore.tst')
162
219
 
163
- it "runs rsync on the file" do
164
- rsync.should_receive(:run).with('removed')
220
+ subject.call(env)
221
+ sleep 0.2
222
+ end
165
223
 
166
- subject.call(env)
167
- sleep 0.2
168
- end
224
+ it "correctly filters glob patterns with **" do
225
+ queue.stub(:pop).and_return(
226
+ { :event => :added, :path => "vendor/my/test/file.tst" },
227
+ { :event => :added, :path => "vendor/my/deep/test/file.tst" },
228
+ { :event => :added, :path => "vendor/test/file.tst" },
229
+ { :quit => true })
230
+
231
+ rsync.should_receive(:run).with('vendor/test/file.tst')
232
+
233
+ subject.call(env)
234
+ sleep 0.2
169
235
  end
170
236
 
171
237
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-mirror
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1.alpha
4
+ version: 0.1.2.alpha
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-02 00:00:00.000000000 Z
12
+ date: 2013-04-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: vagrant