vagrant-syncer 1.0.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 +11 -0
- data/.ruby-version +1 -0
- data/Gemfile +12 -0
- data/LICENSE +21 -0
- data/README.md +81 -0
- data/Rakefile +3 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/build_and_install.sh +3 -0
- data/example/Vagrantfile +61 -0
- data/example/files/directory/excluded_directory/yes.txt +0 -0
- data/example/files/directory/file_in_directory.txt +0 -0
- data/example/files/directory/nested_directory/excluded_directory/yes.txt +0 -0
- data/example/files/directory/nested_directory/excluded_file.txt +0 -0
- data/example/files/directory/nested_directory/excluded_nested_directory/no.txt +0 -0
- data/example/files/directory/nested_directory/excluded_nested_file.txt +0 -0
- data/example/files/directory/nested_directory/yes.txt +0 -0
- data/example/files/directory/nested_directory2/excluded_nested_directory/no.txt +0 -0
- data/example/files/directory/nested_directory2/excluded_nested_file.txt +0 -0
- data/example/files/directory/nested_directory2/yes.txt +0 -0
- data/example/files/directory/sync_me.not +0 -0
- data/example/files/excluded_directory/no.txt +0 -0
- data/example/files/excluded_file.txt +0 -0
- data/example/files/file.not +0 -0
- data/example/files/yes.txt +0 -0
- data/lib/syncer/actions.rb +24 -0
- data/lib/syncer/commands/syncer.rb +22 -0
- data/lib/syncer/config.rb +27 -0
- data/lib/syncer/listeners/fsevents.rb +40 -0
- data/lib/syncer/listeners/inotify.rb +38 -0
- data/lib/syncer/listeners/listen.rb +60 -0
- data/lib/syncer/machine.rb +30 -0
- data/lib/syncer/path.rb +71 -0
- data/lib/syncer/plugin.rb +29 -0
- data/lib/syncer/syncers/rsync.rb +161 -0
- data/lib/syncer/version.rb +5 -0
- data/lib/vagrant-syncer.rb +23 -0
- data/locales/en.yml +8 -0
- data/vagrant-syncer.gemspec +23 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 78956c78a675ea34cb1458cf9a2961a6e1cffe14
|
4
|
+
data.tar.gz: 16a0bc9b8a5a792ea5fda9ee0fc3e095546b4036
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 69bb83ca7c7e906fffde1dcddea27fd0608a0a945f0f70c6c7179b65fae0548d1bbf950d8a282709881032ad4842887f5fb498aba5655c39c545132bc3c50f04
|
7
|
+
data.tar.gz: 479e505688e4886a3709c37586add4a2d1f34cfa9c9f14f4819491069edad763a6227167ce1fd2ef16757a3b5a6855fd8f45b3921efb4d8c6cf46f9602a8421e
|
data/.gitignore
ADDED
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-syncer.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "vagrant", git: "https://github.com/mitchellh/vagrant.git", tag: 'v1.7.4'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :plugins do
|
11
|
+
gem "vagrant-syncer", path: "."
|
12
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2015 Anssi Syrjäsalo
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# vagrant syncer
|
2
|
+
|
3
|
+
A Vagrant synced folder plugin that is an optimized implementation of [Vagrant rsync(-auto)](https://github.com/mitchellh/vagrant/tree/b721eb62cfbfa93895d0d4cf019436ab6b1df05d/plugins/synced_folders/rsync), based heavily on [vagrant-gatling-rsync](https://github.com/smerrill/vagrant-gatling-rsync)'s great listener implementations for watching large hierarchies.
|
4
|
+
|
5
|
+
Vagrant syncer forks [Vagrant's RsyncHelper](https://github.com/mitchellh/vagrant/blob/b721eb62cfbfa93895d0d4cf019436ab6b1df05d/plugins/synced_folders/rsync/helper.rb)
|
6
|
+
to make it (c)leaner, instead of using the class like [vagrant-gatling-rsync](https://github.com/smerrill/vagrant-gatling-rsync) does.
|
7
|
+
|
8
|
+
If the optimizations seem to work in heavy use, I'll see if (some of) them
|
9
|
+
can be merged to Vagrant core and be submitted as pull requests to
|
10
|
+
[the official Vagrant repo](https://github.com/mitchellh/vagrant).
|
11
|
+
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
vagrant plugin install vagrant-syncer
|
16
|
+
|
17
|
+
|
18
|
+
## Configuration
|
19
|
+
|
20
|
+
All the [rsync synced folder settings](https://docs.vagrantup.com/v2/synced-folders/rsync.html)
|
21
|
+
are supported. They also have the same default values.
|
22
|
+
|
23
|
+
See [the example Vagrantfile](https://github.com/asyrjasalo/vagrant-syncer/blob/master/example/Vagrantfile)
|
24
|
+
for additional plugin specific ```config.syncer``` settings and their default values.
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
vagrant syncer
|
30
|
+
|
31
|
+
## Improvements over rsync(-auto)
|
32
|
+
|
33
|
+
- The plugin has leaner rsync implementation with most of the rsync command
|
34
|
+
argument constructing already handled in the class initializer and not sync-time
|
35
|
+
- Uses [rb-fsevent](https://github.com/thibaudgg/rb-fsevent) and
|
36
|
+
[rb-inotify](https://github.com/nex3/rb-inotify) gems underneath for
|
37
|
+
performance on OS X and GNU/Linux respectively, instead of using Listen.
|
38
|
+
On Windows, Listen is used though as using wdm still needs some testing.
|
39
|
+
- Allow defining additional SSH arguments to rsync in Vagrantfile using
|
40
|
+
```config.syncer.ssh_args```. This can be used for e.g. disabling SSH
|
41
|
+
compression to lower CPU overhead.
|
42
|
+
- Runs ```vagrant syncer``` to start watching changes after vagrant up, reload
|
43
|
+
and resume, if ```config.syncer.run_on_startup``` set to ```true```
|
44
|
+
in Vagrantfile
|
45
|
+
- Vagrant's implementation assumes that the primary group of the SSH user
|
46
|
+
has the same name as the user, if rsync option ```group``` is not explicitly
|
47
|
+
defined. This plugin queries the user's real primary group from the guest.
|
48
|
+
- Hooking Vagrant's ```:rsync_pre``` is removed, as this unnecessarily runs mkdir
|
49
|
+
to create the target directory, which rsync command creates sync-time anyway.
|
50
|
+
|
51
|
+
|
52
|
+
## Development
|
53
|
+
|
54
|
+
Fork this repository, clone it and install Ruby 2.2.3, using e.g. [rbenv](https://github.com/sstephenson/rbenv):
|
55
|
+
|
56
|
+
cd vagrant-syncer
|
57
|
+
rbenv install $(cat .ruby-version)
|
58
|
+
gem install bundler -v1.10.5
|
59
|
+
bundle install
|
60
|
+
|
61
|
+
Then use it with:
|
62
|
+
|
63
|
+
bundle exec vagrant syncer
|
64
|
+
|
65
|
+
Or outside the bundle:
|
66
|
+
|
67
|
+
./build_and_install.sh
|
68
|
+
vagrant syncer
|
69
|
+
|
70
|
+
I'll kindly take pull requests as well.
|
71
|
+
|
72
|
+
## Credits
|
73
|
+
|
74
|
+
[vagrant-syncer](https://github.com/asyrjasalo/vagrant-syncer) was originally put together by Anssi Syrjäsalo.
|
75
|
+
|
76
|
+
Thanks to [Steven Merrill's](https://github.com/smerrill) (@stevenmerrill) [vagrant-gatling-rsync](https://github.com/smerrill/vagrant-gatling-rsync)
|
77
|
+
for [the listener implementations](https://github.com/smerrill/vagrant-gatling-rsync/tree/master/lib/vagrant-gatling-rsync/listen) and the original idea to tap into [rb-fsevent](https://github.com/thibaudgg/rb-fsevent) (OS X)
|
78
|
+
and [rb-inotify](https://github.com/nex3/rb-inotify) (GNU/Linux) for non-CPU hog watching of hierarchies with 10,000-100,000 files.
|
79
|
+
|
80
|
+
And to [Hashicorp](https://github.com/hashicorp) for [Vagrant](https://github.com/mitchellh/vagrant), even though its
|
81
|
+
future will likely be overshadowed by [Otto](https://github.com/hashicorp/otto).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "vagrant/syncer"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/example/Vagrantfile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
|
4
|
+
Vagrant.configure(2) do |config|
|
5
|
+
config.vm.box = 'ubuntu/trusty64'
|
6
|
+
|
7
|
+
# Disable checking updates for faster testing
|
8
|
+
config.vm.box_check_update = false
|
9
|
+
config.vbguest.auto_update = false if Vagrant.has_plugin?("vagrant-vbguest")
|
10
|
+
|
11
|
+
config.vm.network 'private_network', ip: '10.10.10.10'
|
12
|
+
|
13
|
+
config.vm.synced_folder '.', '/vagrant', disabled: true
|
14
|
+
|
15
|
+
config.vm.synced_folder "files", "/home/vagrant/files", type: 'rsync',
|
16
|
+
rsync__args: [
|
17
|
+
"--archive",
|
18
|
+
"--delete",
|
19
|
+
"--force",
|
20
|
+
"--numeric-ids"
|
21
|
+
],
|
22
|
+
rsync__exclude: [
|
23
|
+
"/excluded_file.txt",
|
24
|
+
"/excluded_directory",
|
25
|
+
"excluded_nested_file.txt",
|
26
|
+
"excluded_nested_directory",
|
27
|
+
"*.not",
|
28
|
+
".git"
|
29
|
+
],
|
30
|
+
rsync__rsync_path: 'rsync',
|
31
|
+
rsync__verbose: true
|
32
|
+
|
33
|
+
config.ssh.username = 'vagrant'
|
34
|
+
config.ssh.forward_agent = true
|
35
|
+
|
36
|
+
|
37
|
+
### Vagrant syncer specific settings are introduced below
|
38
|
+
|
39
|
+
# How often (in seconds) to read file system events
|
40
|
+
# Default: 0.2. Minimum accepted value is 0.2.
|
41
|
+
config.syncer.interval = 0.2
|
42
|
+
|
43
|
+
# Whether or not to start watching after the machine is up, reloaded or resumed
|
44
|
+
# Default: true
|
45
|
+
config.syncer.run_on_startup = true
|
46
|
+
|
47
|
+
# Whether or not to show the file system events
|
48
|
+
# Default: false
|
49
|
+
config.syncer.show_events = false
|
50
|
+
|
51
|
+
# Optional SSH arguments passed to rsync for lower CPU load
|
52
|
+
# Default: -o StrictHostKeyChecking=no, -o IdentitiesOnly=true, -o UserKnownHostsFile=/dev/null
|
53
|
+
config.syncer.ssh_args = [
|
54
|
+
'-o StrictHostKeyChecking=no',
|
55
|
+
'-o IdentitiesOnly=true',
|
56
|
+
'-o UserKnownHostsFile=/dev/null',
|
57
|
+
'-c arcfour,blowfish-cbc',
|
58
|
+
'-o Compression=no',
|
59
|
+
'-x'
|
60
|
+
]
|
61
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Syncer
|
3
|
+
module Actions
|
4
|
+
class StartSyncer
|
5
|
+
|
6
|
+
def initialize(app, env)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@app.call(env)
|
12
|
+
|
13
|
+
return unless env[:machine].config.syncer.run_on_startup
|
14
|
+
|
15
|
+
# If Vagrant up/reload/resume exited successfully, run this syncer
|
16
|
+
at_exit do
|
17
|
+
env[:machine].env.cli("syncer") if $!.status == 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Syncer
|
3
|
+
module Commands
|
4
|
+
class Syncer < Vagrant.plugin(2, :command)
|
5
|
+
|
6
|
+
def self.synopsis
|
7
|
+
"start auto-rsyncing"
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
with_target_vms do |machine|
|
12
|
+
machine = Machine.new(machine)
|
13
|
+
# machine.full_sync
|
14
|
+
machine.listen
|
15
|
+
end
|
16
|
+
0
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Syncer
|
3
|
+
class Config < Vagrant.plugin(2, :config)
|
4
|
+
|
5
|
+
attr_accessor :interval, :run_on_startup, :show_events, :ssh_args
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@interval = UNSET_VALUE
|
9
|
+
@show_events = UNSET_VALUE
|
10
|
+
@ssh_args = UNSET_VALUE
|
11
|
+
@run_on_startup = UNSET_VALUE
|
12
|
+
end
|
13
|
+
|
14
|
+
def finalize!
|
15
|
+
@interval = 0.2 if @interval == UNSET_VALUE || @interval <= 0.2
|
16
|
+
@run_on_startup = true if @run_on_startup == UNSET_VALUE
|
17
|
+
@show_events = false if @show_events == UNSET_VALUE
|
18
|
+
@ssh_args = [
|
19
|
+
'-o StrictHostKeyChecking=no',
|
20
|
+
'-o IdentitiesOnly=true',
|
21
|
+
'-o UserKnownHostsFile=/dev/null'
|
22
|
+
] if @ssh_args == UNSET_VALUE
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rb-fsevent"
|
2
|
+
|
3
|
+
module Vagrant
|
4
|
+
module Syncer
|
5
|
+
module Listeners
|
6
|
+
class FSEvents
|
7
|
+
|
8
|
+
def initialize(absolute_path, excludes, settings, callback)
|
9
|
+
@absolute_path = absolute_path
|
10
|
+
@settings = settings.merge!(no_defer: false)
|
11
|
+
@callback = callback
|
12
|
+
# rb-fsevent does not support excludes
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
changes = Queue.new
|
17
|
+
fsevent = FSEvent.new
|
18
|
+
fsevent.watch @absolute_path, @settings do |paths|
|
19
|
+
paths.each { |path| changes << path }
|
20
|
+
end
|
21
|
+
Thread.new { fsevent.run }
|
22
|
+
|
23
|
+
loop do
|
24
|
+
directories = Set.new
|
25
|
+
begin
|
26
|
+
loop do
|
27
|
+
change = Timeout::timeout(@settings[:latency]) { changes.pop }
|
28
|
+
directories << change unless change.nil?
|
29
|
+
end
|
30
|
+
rescue Timeout::Error, ThreadError
|
31
|
+
end
|
32
|
+
|
33
|
+
@callback.call(directories.to_a) unless directories.empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rb-inotify"
|
2
|
+
|
3
|
+
module Vagrant
|
4
|
+
module Syncer
|
5
|
+
module Listeners
|
6
|
+
class INotify
|
7
|
+
|
8
|
+
def initialize(absolute_path, excludes, settings, callback)
|
9
|
+
@absolute_path = absolute_path
|
10
|
+
@settings = settings
|
11
|
+
@callback = callback
|
12
|
+
# rb-inotify does not support excludes
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
notifier = ::INotify::Notifier.new
|
17
|
+
notifier.watch(@absolute_path, :modify, :create, :delete, :recursive) {}
|
18
|
+
|
19
|
+
loop do
|
20
|
+
directories = Set.new
|
21
|
+
begin
|
22
|
+
loop do
|
23
|
+
events = []
|
24
|
+
events = Timeout::timeout(@settings[:latency]) {
|
25
|
+
notifier.read_events
|
26
|
+
}
|
27
|
+
events.each { |e| directories << e.absolute_name }
|
28
|
+
end
|
29
|
+
rescue Timeout::Error
|
30
|
+
end
|
31
|
+
|
32
|
+
@callback.call(directories.to_a) unless directories.empty?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'listen'
|
2
|
+
require 'vagrant/util/busy'
|
3
|
+
|
4
|
+
module Vagrant
|
5
|
+
module Syncer
|
6
|
+
module Listeners
|
7
|
+
class Listen
|
8
|
+
|
9
|
+
def self.excludes_to_listen(exclude)
|
10
|
+
exclude = exclude.gsub('**', '"GLOBAL"')
|
11
|
+
exclude = exclude.gsub('*', '"PATH"')
|
12
|
+
|
13
|
+
if exclude.start_with?('/')
|
14
|
+
pattern = "^#{Regexp.escape(exclude[1..-1])}"
|
15
|
+
else
|
16
|
+
pattern = Regexp.escape(exclude)
|
17
|
+
end
|
18
|
+
|
19
|
+
pattern = pattern.gsub('"PATH"', "[^/]*")
|
20
|
+
pattern = pattern.gsub('"GLOBAL"', ".*")
|
21
|
+
|
22
|
+
Regexp.new(pattern)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(absolute_path, excludes, settings, callback)
|
26
|
+
@absolute_path = absolute_path
|
27
|
+
@settings = settings
|
28
|
+
@callback = callback
|
29
|
+
|
30
|
+
if excludes
|
31
|
+
@settings[:ignore!] = []
|
32
|
+
excludes.each do |pattern|
|
33
|
+
@settings[:ignore!] << self.class.excludes_to_listen(pattern.to_s)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
listener = ::Listen.to(@absolute_path, @settings) do |mod, add, rem|
|
41
|
+
@callback.call(mod + add + rem)
|
42
|
+
end
|
43
|
+
|
44
|
+
queue = Queue.new
|
45
|
+
callback = lambda do
|
46
|
+
Thread.new { queue << true }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Run the listener in a busy block, exit once we receive an interrupt
|
50
|
+
Vagrant::Util::Busy.busy(callback) do
|
51
|
+
listener.start
|
52
|
+
queue.pop
|
53
|
+
listener.stop if listener.state != :stopped
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'vagrant/action/builtin/mixin_synced_folders'
|
2
|
+
|
3
|
+
module Vagrant
|
4
|
+
module Syncer
|
5
|
+
class Machine
|
6
|
+
|
7
|
+
include Vagrant::Action::Builtin::MixinSyncedFolders
|
8
|
+
|
9
|
+
def initialize(machine)
|
10
|
+
@paths = []
|
11
|
+
|
12
|
+
synced_folders = synced_folders(machine)[:rsync]
|
13
|
+
return unless synced_folders
|
14
|
+
|
15
|
+
synced_folders.each do |id, folder_opts|
|
16
|
+
@paths << Path.new(folder_opts, machine)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def full_sync
|
21
|
+
@paths.each(&:initial_sync)
|
22
|
+
end
|
23
|
+
|
24
|
+
def listen
|
25
|
+
@paths.each(&:listen)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/syncer/path.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'vagrant/util/platform'
|
2
|
+
|
3
|
+
require_relative 'syncers/rsync'
|
4
|
+
|
5
|
+
module Vagrant
|
6
|
+
module Syncer
|
7
|
+
class Path
|
8
|
+
|
9
|
+
def initialize(path_opts, machine)
|
10
|
+
@logger = machine.ui
|
11
|
+
@source_path = path_opts[:hostpath]
|
12
|
+
@syncer = Syncers::Rsync.new(path_opts, machine)
|
13
|
+
@absolute_path = File.expand_path(@source_path, machine.env.root_path)
|
14
|
+
|
15
|
+
@listener_verbose = machine.config.syncer.show_events
|
16
|
+
@listener_interval = machine.config.syncer.interval
|
17
|
+
|
18
|
+
case Vagrant::Util::Platform.platform
|
19
|
+
when /darwin/
|
20
|
+
require_relative 'listeners/fsevents'
|
21
|
+
@listener_class = Vagrant::Syncer::Listeners::FSEvents
|
22
|
+
when /linux/
|
23
|
+
require_relative 'listeners/inotify'
|
24
|
+
@listener_class = Vagrant::Syncer::Listeners::INotify
|
25
|
+
else
|
26
|
+
require_relative 'listeners/listen'
|
27
|
+
@listener_class = Vagrant::Syncer::Listeners::Listen
|
28
|
+
end
|
29
|
+
|
30
|
+
@listener_name = @listener_class.to_s.gsub(/^.*::/, '')
|
31
|
+
|
32
|
+
listener_settings = {
|
33
|
+
latency: @listener_interval
|
34
|
+
}
|
35
|
+
|
36
|
+
@listener = @listener_class.new(
|
37
|
+
@absolute_path,
|
38
|
+
path_opts[:rsync__excludes],
|
39
|
+
listener_settings,
|
40
|
+
change_callback
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initial_sync
|
45
|
+
@logger.info(I18n.t('syncer.states.initial', path: @absolute_path))
|
46
|
+
@syncer.sync
|
47
|
+
end
|
48
|
+
|
49
|
+
def listen
|
50
|
+
@logger.info(I18n.t('syncer.states.watching', {
|
51
|
+
path: @absolute_path,
|
52
|
+
listener: @listener_name,
|
53
|
+
interval: @listener_interval
|
54
|
+
}))
|
55
|
+
@listener.run
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def change_callback
|
61
|
+
Proc.new do |changed|
|
62
|
+
if @listener_verbose
|
63
|
+
@logger.info(@listener_name + ": " + changed.join(', '))
|
64
|
+
end
|
65
|
+
@syncer.sync(changed)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Syncer
|
3
|
+
class Plugin < Vagrant.plugin(2)
|
4
|
+
|
5
|
+
name "Syncer"
|
6
|
+
|
7
|
+
description <<-DESC
|
8
|
+
Watches for changed files on the host and rsyncs them to the machine.
|
9
|
+
DESC
|
10
|
+
|
11
|
+
config "syncer" do
|
12
|
+
require 'syncer/config'
|
13
|
+
Vagrant::Syncer::Config
|
14
|
+
end
|
15
|
+
|
16
|
+
command "syncer" do
|
17
|
+
require 'syncer/commands/syncer'
|
18
|
+
Vagrant::Syncer::Commands::Syncer
|
19
|
+
end
|
20
|
+
|
21
|
+
["machine_action_up", "machine_action_reload", "machine_action_resume"].each do |action|
|
22
|
+
action_hook "start-syncer", action do |hook|
|
23
|
+
hook.append Vagrant::Syncer::Actions::StartSyncer
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require "vagrant/util/platform"
|
2
|
+
require "vagrant/util/subprocess"
|
3
|
+
|
4
|
+
module Vagrant
|
5
|
+
module Syncer
|
6
|
+
module Syncers
|
7
|
+
class Rsync
|
8
|
+
|
9
|
+
def initialize(path_opts, machine)
|
10
|
+
@machine = machine
|
11
|
+
@logger = machine.ui
|
12
|
+
|
13
|
+
@machine_path = machine.env.root_path.to_s
|
14
|
+
@host_path = parse_host_path(path_opts[:hostpath])
|
15
|
+
@rsync_args = parse_rsync_args(path_opts[:rsync__args], path_opts[:rsync__rsync_path])
|
16
|
+
@rsync_verbose = path_opts[:rsync__verbose] || false
|
17
|
+
@ssh_command = parse_ssh_command(machine.config.syncer.ssh_args)
|
18
|
+
@exclude_args = parse_exclude_args(path_opts[:rsync__exclude])
|
19
|
+
|
20
|
+
ssh_username = machine.ssh_info[:username]
|
21
|
+
ssh_host = machine.ssh_info[:host]
|
22
|
+
@ssh_target = "#{ssh_username}@#{ssh_host}:#{path_opts[:guestpath]}"
|
23
|
+
|
24
|
+
@vagrant_command_opts = {
|
25
|
+
workdir: @machine_path
|
26
|
+
}
|
27
|
+
|
28
|
+
@vagrant_rsync_opts = {
|
29
|
+
guestpath: path_opts[:guestpath],
|
30
|
+
chown: path_opts[:rsync__chown],
|
31
|
+
owner: path_opts[:owner],
|
32
|
+
group: path_opts[:group]
|
33
|
+
}
|
34
|
+
@vagrant_rsync_opts[:chown] ||= true
|
35
|
+
@vagrant_rsync_opts[:owner] ||= ssh_username
|
36
|
+
if @vagrant_rsync_opts[:group].nil?
|
37
|
+
machine.communicate.execute('id -gn') do |type, output|
|
38
|
+
@vagrant_rsync_opts[:group] = output.chomp if type == :stdout
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def sync(changed_paths=nil)
|
44
|
+
changed_paths ||= [@host_path]
|
45
|
+
|
46
|
+
rsync_command = [
|
47
|
+
"rsync",
|
48
|
+
@rsync_args,
|
49
|
+
"-e", @ssh_command,
|
50
|
+
changed_paths.map { |path| ["--include", path] },
|
51
|
+
@exclude_args,
|
52
|
+
@host_path,
|
53
|
+
@ssh_target
|
54
|
+
].flatten
|
55
|
+
|
56
|
+
rsync_vagrant_command = rsync_command + [@vagrant_command_opts]
|
57
|
+
if @rsync_verbose
|
58
|
+
@vagrant_command_opts[:notify] = [:stdout, :stderr]
|
59
|
+
result = Vagrant::Util::Subprocess.execute(*rsync_vagrant_command) do |io_name, data|
|
60
|
+
data.each_line do |line|
|
61
|
+
if io_name == :stdout
|
62
|
+
@logger.success("Rsync: #{line}")
|
63
|
+
elsif io_name == :stderr && !line =~ /Permanently added/
|
64
|
+
@logger.warn("Rsync: #{line}")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
result = Vagrant::Util::Subprocess.execute(*rsync_vagrant_command)
|
70
|
+
end
|
71
|
+
|
72
|
+
if result.exit_code != 0
|
73
|
+
@logger.error(I18n.t('syncer.rsync.failed', error: result.stderr))
|
74
|
+
@logger.error(I18n.t('syncer.rsync.failed_command', command: rsync_command.join(' ')))
|
75
|
+
return
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set owner/group after the files are transferred
|
79
|
+
if @machine.guest.capability?(:rsync_post)
|
80
|
+
@machine.guest.capability(:rsync_post, @vagrant_rsync_opts)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def parse_host_path(host_dir)
|
87
|
+
abs_host_path = File.expand_path(host_dir, @machine_path)
|
88
|
+
abs_host_path = Vagrant::Util::Platform.fs_real_path(abs_host_path).to_s
|
89
|
+
|
90
|
+
# Rsync on Windows expects Cygwin style paths
|
91
|
+
if Vagrant::Util::Platform.windows?
|
92
|
+
abs_host_path = Vagrant::Util::Platform.cygwin_path(abs_host_path)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Ensure path ends with '/' to prevent creating directory inside directory
|
96
|
+
abs_host_path += "/" if !abs_host_path.end_with?("/")
|
97
|
+
|
98
|
+
abs_host_path
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_exclude_args(excludes=nil)
|
102
|
+
excludes ||= []
|
103
|
+
excludes << '.vagrant/' # in any case, exclude .vagrant directory
|
104
|
+
excludes.uniq.map { |e| ["--exclude", e] }
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse_ssh_command(ssh_args)
|
108
|
+
proxy_command = ""
|
109
|
+
if @machine.ssh_info[:proxy_command]
|
110
|
+
proxy_command = "-o ProxyCommand='#{@machine.ssh_info[:proxy_command]}' "
|
111
|
+
end
|
112
|
+
|
113
|
+
ssh_command = [
|
114
|
+
"ssh -p #{@machine.ssh_info[:port]} " +
|
115
|
+
proxy_command +
|
116
|
+
ssh_args.join(' '),
|
117
|
+
@machine.ssh_info[:private_key_path].map { |p| "-i '#{p}'" },
|
118
|
+
].flatten.join(' ')
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_rsync_args(rsync_args=nil, rsync_path=nil)
|
122
|
+
rsync_args ||= ["--archive", "--delete", "--compress", "--copy-links", "--verbose"]
|
123
|
+
|
124
|
+
# This is the default rsync output unless overridden by user
|
125
|
+
rsync_args.unshift("--out-format=%L%n")
|
126
|
+
|
127
|
+
rsync_chmod_args_given = rsync_args.any? { |arg| arg.start_with?("--chmod=") }
|
128
|
+
|
129
|
+
# On Windows, enable all non-masked bits to avoid permission issues
|
130
|
+
if Vagrant::Util::Platform.windows? && !rsync_chmod_args_given
|
131
|
+
rsync_args << "--chmod=ugo=rwX"
|
132
|
+
|
133
|
+
# Remove the -p option if --archive (equals -rlptgoD) is given
|
134
|
+
# Otherwise new files won't get the destination-default permissions
|
135
|
+
if rsync_args.include?("--archive") || rsync_args.include?("-a")
|
136
|
+
rsync_args << "--no-perms"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Disable rsync's owner/group preservation (implied by --archive) unless
|
141
|
+
# explicitly requested, since we adjust owner/group later ourselves
|
142
|
+
unless rsync_args.include?("--owner") || rsync_args.include?("-o")
|
143
|
+
rsync_args << "--no-owner"
|
144
|
+
end
|
145
|
+
unless rsync_args.include?("--group") || rsync_args.include?("-g")
|
146
|
+
rsync_args << "--no-group"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Invoke remote rsync with sudo to allow owner and group settings to work
|
150
|
+
if !rsync_path && @machine.guest.capability?(:rsync_command)
|
151
|
+
rsync_path = @machine.guest.capability(:rsync_command)
|
152
|
+
end
|
153
|
+
rsync_args << "--rsync-path" << rsync_path if rsync_path
|
154
|
+
|
155
|
+
rsync_args
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require "vagrant"
|
3
|
+
rescue LoadError
|
4
|
+
raise "This plugin must be run within Vagrant."
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'syncer/actions'
|
8
|
+
require 'syncer/machine'
|
9
|
+
require 'syncer/path'
|
10
|
+
require 'syncer/plugin'
|
11
|
+
require 'syncer/version'
|
12
|
+
|
13
|
+
module Vagrant
|
14
|
+
module Syncer
|
15
|
+
|
16
|
+
def self.source_root
|
17
|
+
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
18
|
+
end
|
19
|
+
|
20
|
+
I18n.load_path << File.expand_path("locales/en.yml", source_root)
|
21
|
+
I18n.reload!
|
22
|
+
end
|
23
|
+
end
|
data/locales/en.yml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
en:
|
2
|
+
syncer:
|
3
|
+
states:
|
4
|
+
initial: "Syncing %{path} to get the target up to date."
|
5
|
+
watching: "Watching %{path} for changes (via %{listener}) every %{interval} seconds."
|
6
|
+
rsync:
|
7
|
+
failed: "Rsync failed: %{error}"
|
8
|
+
failed_command: "The failed command was: %{command}"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'syncer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "vagrant-syncer"
|
8
|
+
spec.version = Vagrant::Syncer::VERSION
|
9
|
+
spec.authors = ["Anssi Syrjäsalo"]
|
10
|
+
spec.email = ["anssi.syrjasalo@gmail.com"]
|
11
|
+
spec.summary = %q{Optimized Vagrant rsync-auto}
|
12
|
+
spec.description = %q{A Vagrant synced folder plugin with watchers for large file hierarchies and (c)leaner rsync-auto.}
|
13
|
+
spec.homepage = "https://github.com/asyrjasalo/vagrant-syncer"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vagrant-syncer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anssi Syrjäsalo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-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
|
+
description: A Vagrant synced folder plugin with watchers for large file hierarchies
|
42
|
+
and (c)leaner rsync-auto.
|
43
|
+
email:
|
44
|
+
- anssi.syrjasalo@gmail.com
|
45
|
+
executables:
|
46
|
+
- console
|
47
|
+
- setup
|
48
|
+
extensions: []
|
49
|
+
extra_rdoc_files: []
|
50
|
+
files:
|
51
|
+
- ".gitignore"
|
52
|
+
- ".ruby-version"
|
53
|
+
- Gemfile
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- bin/console
|
58
|
+
- bin/setup
|
59
|
+
- build_and_install.sh
|
60
|
+
- example/Vagrantfile
|
61
|
+
- example/files/directory/excluded_directory/yes.txt
|
62
|
+
- example/files/directory/file_in_directory.txt
|
63
|
+
- example/files/directory/nested_directory/excluded_directory/yes.txt
|
64
|
+
- example/files/directory/nested_directory/excluded_file.txt
|
65
|
+
- example/files/directory/nested_directory/excluded_nested_directory/no.txt
|
66
|
+
- example/files/directory/nested_directory/excluded_nested_file.txt
|
67
|
+
- example/files/directory/nested_directory/yes.txt
|
68
|
+
- example/files/directory/nested_directory2/excluded_nested_directory/no.txt
|
69
|
+
- example/files/directory/nested_directory2/excluded_nested_file.txt
|
70
|
+
- example/files/directory/nested_directory2/yes.txt
|
71
|
+
- example/files/directory/sync_me.not
|
72
|
+
- example/files/excluded_directory/no.txt
|
73
|
+
- example/files/excluded_file.txt
|
74
|
+
- example/files/file.not
|
75
|
+
- example/files/yes.txt
|
76
|
+
- lib/syncer/actions.rb
|
77
|
+
- lib/syncer/commands/syncer.rb
|
78
|
+
- lib/syncer/config.rb
|
79
|
+
- lib/syncer/listeners/fsevents.rb
|
80
|
+
- lib/syncer/listeners/inotify.rb
|
81
|
+
- lib/syncer/listeners/listen.rb
|
82
|
+
- lib/syncer/machine.rb
|
83
|
+
- lib/syncer/path.rb
|
84
|
+
- lib/syncer/plugin.rb
|
85
|
+
- lib/syncer/syncers/rsync.rb
|
86
|
+
- lib/syncer/version.rb
|
87
|
+
- lib/vagrant-syncer.rb
|
88
|
+
- locales/en.yml
|
89
|
+
- vagrant-syncer.gemspec
|
90
|
+
homepage: https://github.com/asyrjasalo/vagrant-syncer
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata: {}
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.4.5.1
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: Optimized Vagrant rsync-auto
|
114
|
+
test_files: []
|