vagrant-instant-rsync-auto 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +0 -0
- data/.ruby-version +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +120 -0
- data/LICENSE +21 -0
- data/README.md +24 -0
- data/Rakefile +1 -0
- data/lib/vagrant-instant-rsync-auto.rb +12 -0
- data/lib/vagrant-instant-rsync-auto/command/instant_rsync_auto.rb +241 -0
- data/lib/vagrant-instant-rsync-auto/helper.rb +257 -0
- data/lib/vagrant-instant-rsync-auto/plugin.rb +19 -0
- data/lib/vagrant-instant-rsync-auto/version.rb +5 -0
- data/vagrant-instant-rsync-auto.gemspec +25 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4117f71f6469b9f328afbfcc926054dd2c3b1751
|
4
|
+
data.tar.gz: db01eb256c20cb008295fedad381169c4b35a950
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b298fe657fb8ee55ab6919a0531f19472b1024f541b22e454fa5a3dd56ccf125b3309b737ad915c220d4e5449b91c8ee3d2d2bd581bd05506717ade64b7a03e8
|
7
|
+
data.tar.gz: 1782ff3141f6e2a1eebc070358cfd2dde42ed33a5d878eb1c0bd8320f72288d0a101505fd610cf2651a92ac996d425dcc2daaef98c0c26ad511fcd714d64a037
|
data/.gitignore
ADDED
File without changes
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.3
|
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in vagrant-instant-rsync-auto.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem 'vagrant', git: 'https://github.com/mitchellh/vagrant.git', tag: 'v1.8.1'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :plugins do
|
11
|
+
gem 'vagrant-instant-rsync-auto', path: '.'
|
12
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/mitchellh/vagrant.git
|
3
|
+
revision: c1c00e2f3cf69c579a5aa6922d67bb838a2de9cd
|
4
|
+
tag: v1.8.1
|
5
|
+
specs:
|
6
|
+
vagrant (1.8.1)
|
7
|
+
bundler (>= 1.5.2, <= 1.10.6)
|
8
|
+
childprocess (~> 0.5.0)
|
9
|
+
erubis (~> 2.7.0)
|
10
|
+
hashicorp-checkpoint (~> 0.1.1)
|
11
|
+
i18n (>= 0.6.0, <= 0.8.0)
|
12
|
+
listen (~> 3.0.2)
|
13
|
+
log4r (~> 1.1.9, < 1.1.11)
|
14
|
+
net-scp (~> 1.1.0)
|
15
|
+
net-sftp (~> 2.1)
|
16
|
+
net-ssh (~> 3.0.1)
|
17
|
+
nokogiri (= 1.6.3.1)
|
18
|
+
rb-kqueue (~> 0.2.0)
|
19
|
+
rest-client (>= 1.6.0, < 2.0)
|
20
|
+
wdm (~> 0.1.0)
|
21
|
+
winrm (~> 1.3)
|
22
|
+
winrm-fs (~> 0.2.2)
|
23
|
+
|
24
|
+
PATH
|
25
|
+
remote: .
|
26
|
+
specs:
|
27
|
+
vagrant-instant-rsync-auto (0.1.0)
|
28
|
+
|
29
|
+
GEM
|
30
|
+
remote: https://rubygems.org/
|
31
|
+
specs:
|
32
|
+
builder (3.2.2)
|
33
|
+
childprocess (0.5.9)
|
34
|
+
ffi (~> 1.0, >= 1.0.11)
|
35
|
+
coderay (1.1.1)
|
36
|
+
domain_name (0.5.20160310)
|
37
|
+
unf (>= 0.0.5, < 1.0.0)
|
38
|
+
erubis (2.7.0)
|
39
|
+
ffi (1.9.10)
|
40
|
+
gssapi (1.2.0)
|
41
|
+
ffi (>= 1.0.1)
|
42
|
+
gyoku (1.3.1)
|
43
|
+
builder (>= 2.1.2)
|
44
|
+
hashicorp-checkpoint (0.1.4)
|
45
|
+
http-cookie (1.0.2)
|
46
|
+
domain_name (~> 0.5)
|
47
|
+
httpclient (2.8.0)
|
48
|
+
i18n (0.7.0)
|
49
|
+
json (1.8.3)
|
50
|
+
listen (3.0.8)
|
51
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
52
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
53
|
+
little-plugger (1.1.4)
|
54
|
+
log4r (1.1.10)
|
55
|
+
logging (1.8.2)
|
56
|
+
little-plugger (>= 1.1.3)
|
57
|
+
multi_json (>= 1.8.4)
|
58
|
+
method_source (0.8.2)
|
59
|
+
mime-types (2.99.1)
|
60
|
+
mini_portile (0.6.0)
|
61
|
+
multi_json (1.12.1)
|
62
|
+
net-scp (1.1.2)
|
63
|
+
net-ssh (>= 2.6.5)
|
64
|
+
net-sftp (2.1.2)
|
65
|
+
net-ssh (>= 2.6.5)
|
66
|
+
net-ssh (3.0.2)
|
67
|
+
netrc (0.11.0)
|
68
|
+
nokogiri (1.6.3.1)
|
69
|
+
mini_portile (= 0.6.0)
|
70
|
+
nori (2.6.0)
|
71
|
+
pry (0.10.3)
|
72
|
+
coderay (~> 1.1.0)
|
73
|
+
method_source (~> 0.8.1)
|
74
|
+
slop (~> 3.4)
|
75
|
+
rake (11.1.2)
|
76
|
+
rb-fsevent (0.9.7)
|
77
|
+
rb-inotify (0.9.7)
|
78
|
+
ffi (>= 0.5.0)
|
79
|
+
rb-kqueue (0.2.4)
|
80
|
+
ffi (>= 0.5.0)
|
81
|
+
rest-client (1.8.0)
|
82
|
+
http-cookie (>= 1.0.2, < 2.0)
|
83
|
+
mime-types (>= 1.16, < 3.0)
|
84
|
+
netrc (~> 0.7)
|
85
|
+
rubyntlm (0.4.0)
|
86
|
+
rubyzip (1.2.0)
|
87
|
+
slop (3.6.0)
|
88
|
+
unf (0.1.4)
|
89
|
+
unf_ext
|
90
|
+
unf_ext (0.0.7.2)
|
91
|
+
uuidtools (2.1.5)
|
92
|
+
wdm (0.1.1)
|
93
|
+
winrm (1.3.6)
|
94
|
+
builder (>= 2.1.2)
|
95
|
+
gssapi (~> 1.2)
|
96
|
+
gyoku (~> 1.0)
|
97
|
+
httpclient (~> 2.2, >= 2.2.0.2)
|
98
|
+
logging (>= 1.6.1, < 3.0)
|
99
|
+
nori (~> 2.0)
|
100
|
+
rubyntlm (~> 0.4.0)
|
101
|
+
uuidtools (~> 2.1.2)
|
102
|
+
winrm-fs (0.2.3)
|
103
|
+
erubis (~> 2.7)
|
104
|
+
logging (~> 1.6, >= 1.6.1)
|
105
|
+
rubyzip (~> 1.1)
|
106
|
+
winrm (~> 1.3.0)
|
107
|
+
|
108
|
+
PLATFORMS
|
109
|
+
ruby
|
110
|
+
|
111
|
+
DEPENDENCIES
|
112
|
+
bundler
|
113
|
+
json (~> 1.8.1)
|
114
|
+
pry
|
115
|
+
rake
|
116
|
+
vagrant!
|
117
|
+
vagrant-instant-rsync-auto!
|
118
|
+
|
119
|
+
BUNDLED WITH
|
120
|
+
1.10.5
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Steven Merrill
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# vagrant-instant-rsync-auto
|
2
|
+
|
3
|
+
An rsync watcher for Vagrant 1.5.1+ that's much faster than the native
|
4
|
+
`vagrant rsync-auto` command.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
To get started, you need to have Vagrant 1.5.1 installed on your Linux, Mac, or
|
9
|
+
Windows host machine. To install the plugin, use the following command.
|
10
|
+
|
11
|
+
```bash
|
12
|
+
vagrant plugin install vagrant-instant-rsync-auto
|
13
|
+
```
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
This plugin should behave exactly the same as `vagrant rsync-auto`, and in
|
18
|
+
particular it uses the same configuration. So simply run
|
19
|
+
```bash
|
20
|
+
vagrant instant-rsync-auto```
|
21
|
+
_in lieu_ of your usual `vagrant rsync-auto`, and you should be good to go!
|
22
|
+
|
23
|
+
Hopefully this will make it into Vagrant proper, there's a pull request opened
|
24
|
+
for that: https://github.com/mitchellh/vagrant/pull/7332
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# This file is required because Vagrant's plugin system expects
|
2
|
+
# an eponymous ruby file matching the rubygem.
|
3
|
+
#
|
4
|
+
# So this gem is called 'vagrant-instant-rsync-auto' and thus vagrant tries
|
5
|
+
# to require 'vagrant-instant-rsync-auto'
|
6
|
+
|
7
|
+
require 'vagrant-instant-rsync-auto/plugin'
|
8
|
+
|
9
|
+
module VagrantPlugins
|
10
|
+
module InstantRsyncAuto
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require 'optparse'
|
3
|
+
require "thread"
|
4
|
+
|
5
|
+
require "vagrant/action/builtin/mixin_synced_folders"
|
6
|
+
require "vagrant/util/busy"
|
7
|
+
require "vagrant/util/platform"
|
8
|
+
|
9
|
+
require_relative "../helper"
|
10
|
+
|
11
|
+
# This is to avoid a bug in nio 1.0.0. Remove around nio 1.0.1
|
12
|
+
if Vagrant::Util::Platform.windows?
|
13
|
+
ENV["NIO4R_PURE"] = "1"
|
14
|
+
end
|
15
|
+
|
16
|
+
require "listen"
|
17
|
+
|
18
|
+
module VagrantPlugins
|
19
|
+
module InstantRsyncAuto
|
20
|
+
module Command
|
21
|
+
class RsyncAuto < Vagrant.plugin('2', :command)
|
22
|
+
include Vagrant::Action::Builtin::MixinSyncedFolders
|
23
|
+
|
24
|
+
def self.synopsis
|
25
|
+
"syncs rsync synced folders automatically when files change, faster than Vagrant's native rsync-auto"
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute
|
29
|
+
@logger = Log4r::Logger.new("vagrant::commands::instant-rsync-auto")
|
30
|
+
@rsync_helpers = {}
|
31
|
+
|
32
|
+
options = {}
|
33
|
+
opts = OptionParser.new do |o|
|
34
|
+
o.banner = "Usage: vagrant instant-rsync-auto [vm-name]"
|
35
|
+
o.separator ""
|
36
|
+
o.separator "Options:"
|
37
|
+
o.separator ""
|
38
|
+
|
39
|
+
o.on("--[no-]poll", "Force polling filesystem (slow)") do |poll|
|
40
|
+
options[:poll] = poll
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Parse the options and return if we don't have any target.
|
45
|
+
argv = parse_options(opts)
|
46
|
+
return if !argv
|
47
|
+
|
48
|
+
# Build up the paths that we need to listen to.
|
49
|
+
paths = {}
|
50
|
+
ignores = []
|
51
|
+
with_target_vms(argv) do |machine|
|
52
|
+
if machine.provider.capability?(:proxy_machine)
|
53
|
+
proxy = machine.provider.capability(:proxy_machine)
|
54
|
+
if proxy
|
55
|
+
machine.ui.warn(I18n.t(
|
56
|
+
"vagrant.rsync_proxy_machine",
|
57
|
+
name: machine.name.to_s,
|
58
|
+
provider: machine.provider_name.to_s))
|
59
|
+
|
60
|
+
machine = proxy
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
cached = synced_folders(machine, cached: true)
|
65
|
+
fresh = synced_folders(machine)
|
66
|
+
diff = synced_folders_diff(cached, fresh)
|
67
|
+
if !diff[:added].empty?
|
68
|
+
machine.ui.warn(I18n.t("vagrant.rsync_auto_new_folders"))
|
69
|
+
end
|
70
|
+
|
71
|
+
folders = cached[:rsync]
|
72
|
+
next if !folders || folders.empty?
|
73
|
+
|
74
|
+
# Get the SSH info for this machine so we can do an initial
|
75
|
+
# sync to the VM.
|
76
|
+
ssh_info = machine.ssh_info
|
77
|
+
if ssh_info
|
78
|
+
machine.ui.info(I18n.t("vagrant.rsync_auto_initial"))
|
79
|
+
folders.each do |id, folder_opts|
|
80
|
+
rsync_helper(machine, id, folder_opts).rsync_single
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
folders.each do |id, folder_opts|
|
85
|
+
# If we marked this folder to not auto sync, then
|
86
|
+
# don't do it.
|
87
|
+
next if folder_opts.key?(:auto) && !folder_opts[:auto]
|
88
|
+
|
89
|
+
hostpath = folder_opts[:hostpath]
|
90
|
+
hostpath = File.expand_path(hostpath, machine.env.root_path)
|
91
|
+
paths[hostpath] ||= []
|
92
|
+
paths[hostpath] << {
|
93
|
+
id: id,
|
94
|
+
machine: machine,
|
95
|
+
opts: folder_opts,
|
96
|
+
}
|
97
|
+
|
98
|
+
if folder_opts[:exclude]
|
99
|
+
Array(folder_opts[:exclude]).each do |pattern|
|
100
|
+
ignores << RsyncHelper.exclude_to_regexp(hostpath, pattern.to_s)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Exit immediately if there is nothing to watch
|
107
|
+
if paths.empty?
|
108
|
+
@env.ui.info(I18n.t("vagrant.rsync_auto_no_paths"))
|
109
|
+
return 1
|
110
|
+
end
|
111
|
+
|
112
|
+
# Output to the user what paths we'll be watching
|
113
|
+
paths.keys.sort.each do |path|
|
114
|
+
paths[path].each do |path_opts|
|
115
|
+
path_opts[:machine].ui.info(I18n.t(
|
116
|
+
"vagrant.rsync_auto_path",
|
117
|
+
path: path.to_s,
|
118
|
+
))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@logger.info("Listening to paths: #{paths.keys.sort.inspect}")
|
123
|
+
@logger.info("Ignoring #{ignores.length} paths:")
|
124
|
+
ignores.each do |ignore|
|
125
|
+
@logger.info(" -- #{ignore.to_s}")
|
126
|
+
end
|
127
|
+
@logger.info("Listening via: #{Listen::Adapter.select.inspect}")
|
128
|
+
callback = method(:callback).to_proc.curry[paths]
|
129
|
+
listopts = { ignore: ignores, force_polling: !!options[:poll] }
|
130
|
+
listener = Listen.to(*paths.keys, listopts, &callback)
|
131
|
+
|
132
|
+
# Create the callback that lets us know when we've been interrupted
|
133
|
+
queue = Queue.new
|
134
|
+
callback = lambda do
|
135
|
+
# This needs to execute in another thread because Thread
|
136
|
+
# synchronization can't happen in a trap context.
|
137
|
+
Thread.new { queue << true }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Run the listener in a busy block so that we can cleanly
|
141
|
+
# exit once we receive an interrupt.
|
142
|
+
Vagrant::Util::Busy.busy(callback) do
|
143
|
+
listener.start
|
144
|
+
queue.pop
|
145
|
+
listener.stop if listener.state != :stopped
|
146
|
+
end
|
147
|
+
|
148
|
+
0
|
149
|
+
end
|
150
|
+
|
151
|
+
# This is the callback that is called when any changes happen
|
152
|
+
def callback(paths, modified, added, removed)
|
153
|
+
@logger.info("File change callback called!")
|
154
|
+
@logger.info(" - Modified: #{modified.inspect}")
|
155
|
+
@logger.info(" - Added: #{added.inspect}")
|
156
|
+
@logger.info(" - Removed: #{removed.inspect}")
|
157
|
+
|
158
|
+
tosync = []
|
159
|
+
paths.each do |hostpath, folders|
|
160
|
+
# Find out if this path should be synced
|
161
|
+
found = catch(:done) do
|
162
|
+
[modified, added, removed].each do |changed|
|
163
|
+
changed.each do |listenpath|
|
164
|
+
throw :done, true if listenpath.start_with?(hostpath)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Make sure to return false if all else fails so that we
|
169
|
+
# don't sync to this machine.
|
170
|
+
false
|
171
|
+
end
|
172
|
+
|
173
|
+
# If it should be synced, store it for later
|
174
|
+
tosync << folders if found
|
175
|
+
end
|
176
|
+
|
177
|
+
# Sync all the folders that need to be synced
|
178
|
+
tosync.each do |folders|
|
179
|
+
folders.each do |opts|
|
180
|
+
# Reload so we get the latest ID
|
181
|
+
opts[:machine].reload
|
182
|
+
if !opts[:machine].id || opts[:machine].id == ""
|
183
|
+
# Skip since we can't get SSH info without an ID
|
184
|
+
next
|
185
|
+
end
|
186
|
+
|
187
|
+
ssh_info = opts[:machine].ssh_info
|
188
|
+
begin
|
189
|
+
start = Time.now
|
190
|
+
rsync_helper(opts[:machine], opts[:id], opts[:opts]).rsync_single
|
191
|
+
finish = Time.now
|
192
|
+
time_spent_msg = "Time spent in rsync: #{finish-start} (in seconds)"
|
193
|
+
@logger.info(time_spent_msg)
|
194
|
+
opts[:machine].ui.info(time_spent_msg)
|
195
|
+
rescue Vagrant::Errors::MachineGuestNotReady
|
196
|
+
# Error communicating to the machine, probably a reload or
|
197
|
+
# halt is happening. Just notify the user but don't fail out.
|
198
|
+
opts[:machine].ui.error(I18n.t(
|
199
|
+
"vagrant.rsync_communicator_not_ready_callback"))
|
200
|
+
rescue Vagrant::Errors::RSyncError => e
|
201
|
+
# Error executing rsync, so show an error
|
202
|
+
opts[:machine].ui.error(I18n.t(
|
203
|
+
"vagrant.rsync_auto_rsync_error", message: e.to_s))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def rsync_helper(machine, folder_id, folder_opts)
|
210
|
+
machine_helpers = rsync_helpers_for_machine(machine)
|
211
|
+
|
212
|
+
rsync_helpers_for_id(machine, folder_id, folder_opts, machine_helpers)
|
213
|
+
end
|
214
|
+
|
215
|
+
def rsync_helpers_for_machine(machine)
|
216
|
+
@rsync_helpers[machine.id] ||= {}
|
217
|
+
end
|
218
|
+
|
219
|
+
def rsync_helpers_for_id(machine, folder_id, folder_opts, machine_helpers)
|
220
|
+
unless machine_helpers.key?(folder_id)
|
221
|
+
rsync_helper = nil
|
222
|
+
ssh_info = machine.ssh_info
|
223
|
+
|
224
|
+
if ssh_info
|
225
|
+
folder_opts = folder_opts.merge(
|
226
|
+
skip_rsync_pre_after_first_sync: true,
|
227
|
+
skip_rsync_post_after_first_sync: true
|
228
|
+
)
|
229
|
+
|
230
|
+
rsync_helper = RsyncHelper.new(machine, ssh_info, folder_opts)
|
231
|
+
end
|
232
|
+
|
233
|
+
machine_helpers[folder_id] = rsync_helper
|
234
|
+
end
|
235
|
+
|
236
|
+
machine_helpers[folder_id]
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
require "vagrant/util/platform"
|
2
|
+
require "vagrant/util/subprocess"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module InstantRsyncAuto
|
6
|
+
# This is a helper that abstracts out the functionality of rsyncing
|
7
|
+
# folders so that it can be called from anywhere.
|
8
|
+
class RsyncHelper
|
9
|
+
def initialize(machine, ssh_info, opts)
|
10
|
+
@machine = machine
|
11
|
+
|
12
|
+
@opts = normalize_opts(opts, ssh_info)
|
13
|
+
|
14
|
+
@guestpath = guestpath
|
15
|
+
@hostpath = hostpath
|
16
|
+
@excludes = excludes
|
17
|
+
|
18
|
+
@rsync_command = rsync_command(ssh_info)
|
19
|
+
@rsync_command_opts = rsync_command_opts
|
20
|
+
|
21
|
+
@first_sync_done = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def rsync_single
|
25
|
+
log_info
|
26
|
+
|
27
|
+
# If we have tasks to do before rsyncing, do those.
|
28
|
+
if !skip_rsync_pre? && @machine.guest.capability?(:rsync_pre)
|
29
|
+
@machine.guest.capability(:rsync_pre, @opts)
|
30
|
+
end
|
31
|
+
|
32
|
+
subprocess = if verbose?
|
33
|
+
Vagrant::Util::Subprocess.execute(*(@rsync_command + [@rsync_command_opts])) {
|
34
|
+
|io_name,data| data.each_line { |line|
|
35
|
+
@machine.ui.info("rsync[#{io_name}] -> #{line}") }
|
36
|
+
}
|
37
|
+
else
|
38
|
+
Vagrant::Util::Subprocess.execute(*(@rsync_command + [@rsync_command_opts]))
|
39
|
+
end
|
40
|
+
|
41
|
+
if subprocess.exit_code != 0
|
42
|
+
raise Vagrant::Errors::RSyncError,
|
43
|
+
command: @rsync_command.join(" "),
|
44
|
+
guestpath: @guestpath,
|
45
|
+
hostpath: @hostpath,
|
46
|
+
stderr: subprocess.stderr
|
47
|
+
end
|
48
|
+
|
49
|
+
# If we have tasks to do after rsyncing, do those.
|
50
|
+
if !skip_rsync_post? && @machine.guest.capability?(:rsync_post)
|
51
|
+
@machine.guest.capability(:rsync_post, @opts)
|
52
|
+
end
|
53
|
+
|
54
|
+
@first_sync_done = true
|
55
|
+
end
|
56
|
+
|
57
|
+
# This converts an rsync exclude pattern to a regular expression
|
58
|
+
# we can send to Listen.
|
59
|
+
def self.exclude_to_regexp(path, exclude)
|
60
|
+
start_anchor = false
|
61
|
+
|
62
|
+
if exclude.start_with?("/")
|
63
|
+
start_anchor = true
|
64
|
+
exclude = exclude[1..-1]
|
65
|
+
end
|
66
|
+
|
67
|
+
path = "#{path}/" if !path.end_with?("/")
|
68
|
+
regexp = "^#{Regexp.escape(path)}"
|
69
|
+
regexp += ".*" if !start_anchor
|
70
|
+
|
71
|
+
# This is REALLY ghetto, but its a start. We can improve and
|
72
|
+
# keep unit tests passing in the future.
|
73
|
+
exclude = exclude.gsub("**", "|||GLOBAL|||")
|
74
|
+
exclude = exclude.gsub("*", "|||PATH|||")
|
75
|
+
exclude = exclude.gsub("|||PATH|||", "[^/]*")
|
76
|
+
exclude = exclude.gsub("|||GLOBAL|||", ".*")
|
77
|
+
regexp += exclude
|
78
|
+
|
79
|
+
Regexp.new(regexp)
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.rsync_single(machine, ssh_info, opts)
|
83
|
+
machine.ui.warn("WARNING: `VagrantPlugins::SyncedFolderRSync::RsyncHelper.rsync_single`")
|
84
|
+
machine.ui.warn("is a deprecated internal API. Please use an instance of that class instead.")
|
85
|
+
|
86
|
+
instance = new(machine, ssh_info, opts)
|
87
|
+
instance.rsync_single
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def verbose?
|
93
|
+
@opts.include?(:verbose)
|
94
|
+
end
|
95
|
+
|
96
|
+
def normalize_opts(opts, ssh_info)
|
97
|
+
# Folder options
|
98
|
+
opts[:owner] ||= ssh_info[:username]
|
99
|
+
opts[:group] ||= ssh_info[:username]
|
100
|
+
|
101
|
+
opts
|
102
|
+
end
|
103
|
+
|
104
|
+
def guestpath
|
105
|
+
# if the guest has a guest path scrubber capability, use it
|
106
|
+
if @machine.guest.capability?(:rsync_scrub_guestpath)
|
107
|
+
@machine.guest.capability(:rsync_scrub_guestpath, @opts)
|
108
|
+
else
|
109
|
+
@opts[:guestpath]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def hostpath
|
114
|
+
hostpath = @opts[:hostpath]
|
115
|
+
hostpath = File.expand_path(hostpath, @machine.env.root_path)
|
116
|
+
hostpath = Vagrant::Util::Platform.fs_real_path(hostpath).to_s
|
117
|
+
|
118
|
+
if Vagrant::Util::Platform.windows?
|
119
|
+
# rsync for Windows expects cygwin style paths, always.
|
120
|
+
hostpath = Vagrant::Util::Platform.cygwin_path(hostpath)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Make sure the host path ends with a "/" to avoid creating
|
124
|
+
# a nested directory...
|
125
|
+
if !hostpath.end_with?("/")
|
126
|
+
hostpath += "/"
|
127
|
+
end
|
128
|
+
|
129
|
+
hostpath
|
130
|
+
end
|
131
|
+
|
132
|
+
# Builds up the actual command to execute
|
133
|
+
def rsync_command(ssh_info)
|
134
|
+
[
|
135
|
+
"rsync",
|
136
|
+
rsync_args,
|
137
|
+
"-e", rsync_ssh_command(ssh_info),
|
138
|
+
@excludes.map { |e| ["--exclude", e] },
|
139
|
+
@hostpath,
|
140
|
+
"#{ssh_connection_info(ssh_info)}:#{@guestpath}",
|
141
|
+
].flatten
|
142
|
+
end
|
143
|
+
|
144
|
+
def rsync_ssh_command(ssh_info)
|
145
|
+
# Create the path for the control sockets. We used to do this
|
146
|
+
# in the machine data dir but this can result in paths that are
|
147
|
+
# too long for unix domain sockets.
|
148
|
+
controlpath = File.join(Dir.tmpdir, "ssh.#{rand(1000)}")
|
149
|
+
|
150
|
+
[
|
151
|
+
"ssh -p #{ssh_info[:port]} " +
|
152
|
+
proxy_command(ssh_info) +
|
153
|
+
"-o ControlMaster=auto " +
|
154
|
+
"-o ControlPath=#{controlpath} " +
|
155
|
+
"-o ControlPersist=10m " +
|
156
|
+
"-o StrictHostKeyChecking=no " +
|
157
|
+
"-o IdentitiesOnly=true " +
|
158
|
+
"-o UserKnownHostsFile=/dev/null",
|
159
|
+
private_key_paths(ssh_info),
|
160
|
+
].flatten.join(' ')
|
161
|
+
end
|
162
|
+
|
163
|
+
def proxy_command(ssh_info)
|
164
|
+
if ssh_info[:proxy_command]
|
165
|
+
"-o ProxyCommand='#{ssh_info[:proxy_command]}' "
|
166
|
+
else
|
167
|
+
''
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def private_key_paths(ssh_info)
|
172
|
+
ssh_info[:private_key_path].map { |p| "-i '#{p}'" }
|
173
|
+
end
|
174
|
+
|
175
|
+
def ssh_connection_info(ssh_info)
|
176
|
+
username = ssh_info[:username]
|
177
|
+
host = ssh_info[:host]
|
178
|
+
|
179
|
+
"#{username}@#{host}"
|
180
|
+
end
|
181
|
+
|
182
|
+
# Exclude some files by default, and any that might be configured
|
183
|
+
# by the user.
|
184
|
+
def excludes
|
185
|
+
excludes = ['.vagrant/']
|
186
|
+
excludes += Array(@opts[:exclude]).map(&:to_s) if @opts[:exclude]
|
187
|
+
excludes.uniq
|
188
|
+
end
|
189
|
+
|
190
|
+
# Builds the command-line arguments for rsync
|
191
|
+
def rsync_args
|
192
|
+
args = nil
|
193
|
+
args = Array(@opts[:args]).dup if @opts[:args]
|
194
|
+
args ||= ['--verbose', '--archive', '--delete', '-z', '--copy-links']
|
195
|
+
|
196
|
+
# On Windows, we have to set a default chmod flag to avoid permission issues
|
197
|
+
if Vagrant::Util::Platform.windows? && !args.any? { |arg| arg.start_with?('--chmod=') }
|
198
|
+
# Ensures that all non-masked bits get enabled
|
199
|
+
args << '--chmod=ugo=rwX'
|
200
|
+
|
201
|
+
# Remove the -p option if --archive is enabled (--archive equals -rlptgoD)
|
202
|
+
# otherwise new files will not have the destination-default permissions
|
203
|
+
args << '--no-perms' if args.include?('--archive') || args.include?('-a')
|
204
|
+
end
|
205
|
+
|
206
|
+
# Disable rsync's owner/group preservation (implied by --archive) unless
|
207
|
+
# specifically requested, since we adjust owner/group to match shared
|
208
|
+
# folder setting ourselves.
|
209
|
+
args << '--no-owner' unless args.include?('--owner') || args.include?('-o')
|
210
|
+
args << '--no-group' unless args.include?('--group') || args.include?('-g')
|
211
|
+
|
212
|
+
# Tell local rsync how to invoke remote rsync with sudo
|
213
|
+
rsync_path = @opts[:rsync_path]
|
214
|
+
if !rsync_path && @machine.guest.capability?(:rsync_command)
|
215
|
+
rsync_path = @machine.guest.capability(:rsync_command)
|
216
|
+
end
|
217
|
+
if rsync_path
|
218
|
+
args << "--rsync-path"<< rsync_path
|
219
|
+
end
|
220
|
+
|
221
|
+
args
|
222
|
+
end
|
223
|
+
|
224
|
+
def rsync_command_opts
|
225
|
+
# The working directory should be the root path
|
226
|
+
command_opts = {}
|
227
|
+
command_opts[:workdir] = @machine.env.root_path.to_s
|
228
|
+
|
229
|
+
if verbose?
|
230
|
+
command_opts[:notify] = [:stdout, :stderr]
|
231
|
+
end
|
232
|
+
|
233
|
+
command_opts
|
234
|
+
end
|
235
|
+
|
236
|
+
def log_info
|
237
|
+
@machine.ui.info(I18n.t(
|
238
|
+
"vagrant.rsync_folder", guestpath: @guestpath, hostpath: @hostpath))
|
239
|
+
if excludes.length > 1
|
240
|
+
@machine.ui.info(I18n.t(
|
241
|
+
"vagrant.rsync_folder_excludes", excludes: @excludes.inspect))
|
242
|
+
end
|
243
|
+
if verbose?
|
244
|
+
@machine.ui.info(I18n.t("vagrant.rsync_showing_output"));
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def skip_rsync_pre?
|
249
|
+
@opts[:skip_rsync_pre] || @first_sync_done && @opts[:skip_rsync_pre_after_first_sync]
|
250
|
+
end
|
251
|
+
|
252
|
+
def skip_rsync_post?
|
253
|
+
@opts[:skip_rsync_post] || @first_sync_done && @opts[:skip_rsync_post_after_first_sync]
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'vagrant'
|
3
|
+
rescue LoadError
|
4
|
+
raise 'The Vagrant instant-rsync-auto plugin must be run within Vagrant.'
|
5
|
+
end
|
6
|
+
|
7
|
+
module VagrantPlugins
|
8
|
+
module InstantRsyncAuto
|
9
|
+
class Plugin < Vagrant.plugin('2')
|
10
|
+
name 'Instant Rsync Auto'
|
11
|
+
description 'A vagrant plugin that does the same as `vagrant rsync-auto`, except much faster!'
|
12
|
+
|
13
|
+
command 'instant-rsync-auto' do
|
14
|
+
require_relative 'command/instant_rsync_auto'
|
15
|
+
Command::RsyncAuto
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'vagrant-instant-rsync-auto/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'vagrant-instant-rsync-auto'
|
8
|
+
spec.version = VagrantPlugins::InstantRsyncAuto::VERSION
|
9
|
+
spec.authors = ['Jean Rouge']
|
10
|
+
spec.email = ['jer329@cornell.edu']
|
11
|
+
spec.summary = %q{A faster alternative to `vagrant rsync-auto`}
|
12
|
+
spec.homepage = 'https://github.com/wk8/vagrant-instant-rsync-auto'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject {|f| f.start_with?('example/files')}
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'pry'
|
23
|
+
# Make Vagrant work on Linux for development.
|
24
|
+
spec.add_development_dependency 'json', '~> 1.8.1'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vagrant-instant-rsync-auto
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jean Rouge
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.8.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.8.1
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- jer329@cornell.edu
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".ruby-version"
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
80
|
+
- LICENSE
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- lib/vagrant-instant-rsync-auto.rb
|
84
|
+
- lib/vagrant-instant-rsync-auto/command/instant_rsync_auto.rb
|
85
|
+
- lib/vagrant-instant-rsync-auto/helper.rb
|
86
|
+
- lib/vagrant-instant-rsync-auto/plugin.rb
|
87
|
+
- lib/vagrant-instant-rsync-auto/version.rb
|
88
|
+
- vagrant-instant-rsync-auto.gemspec
|
89
|
+
homepage: https://github.com/wk8/vagrant-instant-rsync-auto
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.4.5.1
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: A faster alternative to `vagrant rsync-auto`
|
113
|
+
test_files: []
|