vagrant-instant-rsync-auto 0.1.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 +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: []
|