vagrant-mirror 0.1.1.alpha → 0.1.2.alpha

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