vagrant-fsevents 0.4.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e0a6d6d376834556d231f0e1376359fa89dd630d7fe6b83e3085a438761ecd53
4
+ data.tar.gz: f41efcf688d7db03e6c4c97de5d86cfdae48da94cc530ecdea9291e3fd4d4eb7
5
+ SHA512:
6
+ metadata.gz: 64788d3f168c09d03fd19389cebeb49bf45af4508d2d82d11d8b36dd0913a42be3c135c1644e9f8752ed66b2d38fc1cd2d39daffe690ecfb0ae9bd2f5613ace2
7
+ data.tar.gz: afb788a561c4be1b5d203b1a299096d7434dda51a1ab13c37d60e7ad083dfbe18501ca7a101112a876f3792abd270f6f9583269b8575ff62f4770e3031d9a8fa
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /.history
4
+ /.vscode
5
+ /Gemfile.lock
6
+ /_yardoc/
7
+ /coverage/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+
13
+ .vagrant/
14
+ *.gem
15
+ vagrant.log
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ Layout/EndOfLine:
2
+ EnforcedStyle: lf
3
+
4
+ Naming/FileName:
5
+ Enabled: false
6
+
7
+ Style/RaiseArgs:
8
+ Enabled: false
data/AUTHORS ADDED
@@ -0,0 +1,6 @@
1
+ # This file lists all individuals having contributed content to the repository.
2
+
3
+ Adrien Kohlbecker <adrien.kohlbecker@gmail.com>
4
+ Don Morrison <github@elskwid.net>
5
+ Leandro Facchinetti <me@leafac.com>
6
+ Wolf Hatch <wolf@mechrus.com>
data/CHANGELOG.md ADDED
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ ## 0.4.1 - 2019-03-07
4
+
5
+ - File change lock-out scales with number of files changed (+0.02s/file)
6
+ - Touch and rm commands on guest machine now sent at the same time
7
+
8
+ ## 0.4.0 - 2019-03-06
9
+
10
+ - Renamed to vagrant-fsevents
11
+ - Updated readme
12
+ - Applied Rubocop linting
13
+ - Extensive refactor/restructure
14
+
15
+ ## 0.3.0 - 2016-04-13
16
+
17
+ - Fix for "No such file or directory" on Windows host (#11)
18
+
19
+ ## 0.2.0 - 2015-07-14
20
+
21
+ - *Breaking change:* Minimum required Vagrant version changed to 1.7.3+
22
+ - Fixed a dependency issue with celluloid (#10, @leafac)
23
+
24
+ ## 0.1.1 - 2015-07-05 (yanked)
25
+
26
+ Note: This release was yanked due to a dependency issue
27
+
28
+ - Added command synopsis (#7, @leafac)
29
+ - Fix issue with vagrant runtime dependency on celluloid (#8, @leafac)
30
+
31
+ ## 0.1.0 - 2015-07-05 (yanked)
32
+
33
+ Note: This release was yanked due to a dependency issue
34
+
35
+ - *Breaking change:* Added support for forwarding file addition/removal (#6, @leafac)
36
+ - Depend on `vagrant` rather than `listen` for better compatibility with upstream (#5, @leafac)
37
+ - Added documentation (#3 & #4, @leafac)
38
+
39
+ ## 0.0.6 - 2015-04-16
40
+
41
+ - Fix multimachine use, allow specifying which machine to target (@elskwid)
42
+
43
+ ## 0.0.5 - 2015-04-11
44
+
45
+ - Fix `listen` dependency
46
+
47
+ ## 0.0.4 - 2014-08-18
48
+
49
+ - Uses access time modification, rather than modified time
50
+
51
+ ## 0.0.3 - 2014-08-15
52
+
53
+ - Allow guest path overriding
54
+
55
+ ## 0.0.2 - 2014-08-15
56
+
57
+ - Increase the delay between changes to 2 seconds
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development do
4
+ gem "vagrant", git: "https://github.com/hashicorp/vagrant.git"
5
+ end
6
+
7
+ group :plugins do
8
+ gem "vagrant-fsevents", path: "."
9
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Vagrant-fsnotify copyright (c) 2015-2016 Adrien Kohlbecker
4
+ Vagrant-fsevents copyright (c) 2019 Wolf Hatch
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # vagrant-fsevents
2
+
3
+ Activatable filesystem change forwarding to your [Vagrant][vagrant] VM.
4
+
5
+ ## Problem this solves
6
+
7
+ When developing inside [VirtualBox][virtualbox]-based VMs with synced folders,
8
+ filesystem events in the host system do not propagate to processes running inside
9
+ the VM. This causes difficulty for some development environments, particularly
10
+ when using packagers such as Webpack, as they do not update/rebuild in response
11
+ to editor changes.
12
+
13
+ There are a number of partial solutions in the wild, but none are perfect in all
14
+ circumstances. This project builds on [`vagrant-fsnotify`][vagrant-fsnotify] by
15
+ @adrienkohlbecker to provide a simple on-demand, low resource, and highly
16
+ transparent solution. It is particularly effective for a standard development
17
+ VM which is used widely by multiple teams where perhaps only a few people even
18
+ need filesystem event forwarding, and it's not desirable to have an always-on
19
+ solution that hogs system resources.
20
+
21
+ ## Mechanism
22
+
23
+ `vagrant-fsevents` runs a process listening for filesystem changes on the host
24
+ that affect shared folders, and forwards them to the VM as shell commands such
25
+ as `touch` (and `rm`, as appropriate) using Vagrant's VM administration API.
26
+
27
+ ## Caveats
28
+
29
+ Inconveniently, while filesystem events are not properly forwarded into the VM,
30
+ they're forwarded back out without issue, so every time this plugin triggers an
31
+ update in the VM, a second filesystem event is fired in the host system. To
32
+ prevent the obvious infinite-loop issue this could cause, a minimum 2-second
33
+ per-file dead-zone is used after each update event (with additional time for
34
+ updates with many files changed)
35
+
36
+ This is unfortunately only a partial solution, and large filesystem changes such
37
+ as checking out another git branch can still cause issues such as infinite
38
+ notification loops and accidental creation of blank files that should have been
39
+ removed. However, these issues are usually minor, easily fixed via git, and can
40
+ be entirely avoided by killing the monitor process before major changes.
41
+
42
+ ## Installation
43
+
44
+ `vagrant-fsevents` is a [Vagrant][vagrant] plugin and can be installed by
45
+ running:
46
+
47
+ ```console
48
+ $ vagrant plugin install vagrant-fsevents
49
+ ```
50
+
51
+ [Vagrant][vagrant] version 1.7.3 or greater is required.
52
+
53
+ ## Usage
54
+
55
+ ### Basic setup
56
+
57
+ In `Vagrantfile` synced folder configuration, add the `fsevents: true`
58
+ option. For example, in order to enable `vagrant-fsevents` for the the default
59
+ `/vagrant` shared folder, add the following:
60
+
61
+ ```ruby
62
+ config.vm.synced_folder ".", "/vagrant", fsevents: true
63
+ ```
64
+
65
+ When the guest virtual machine is up, run the following:
66
+
67
+ ```console
68
+ $ vagrant fsevents
69
+ ```
70
+
71
+ This starts the long running process that captures filesystem events on the host
72
+ and forwards them to the guest virtual machine.
73
+
74
+ ### Multi-VM environments
75
+
76
+ In multi-VM environments, you can specify the name of the VMs targeted by
77
+ `vagrant-fsevents` using:
78
+
79
+ ```console
80
+ $ vagrant fsevents <vm-name-1> <vm-name-2> ...
81
+ ```
82
+
83
+ ### Excluding files
84
+
85
+ To exclude files or directories from being watched, you can add an `:exclude`
86
+ option, which takes an array of strings (matched as a regexp against relative
87
+ paths):
88
+
89
+ ```ruby
90
+ config.vm.synced_folder ".", "/vagrant", fsevents: true,
91
+ exclude: ["path1", "some/directory"]
92
+ ```
93
+
94
+ This will exclude all files inside the `path1` and `some/directory`. It will
95
+ also exclude files such as `another/directory/path1`.
96
+
97
+ ### Guest path override
98
+
99
+ If your actual path on the VM is not the same as the one in `synced_folder`, for
100
+ example when using [`vagrant-bindfs`][vagrant-bindfs], you can use the
101
+ `:override_guestpath` option:
102
+
103
+ ```ruby
104
+ config.vm.synced_folder ".", "/vagrant", fsevents: true,
105
+ override_guestpath: "/real/path"
106
+ ```
107
+
108
+ This will forward a notification on `./myfile` to `/real/path/myfile` instead of
109
+ `/vagrant/myfile`.
110
+
111
+ ### Select filesystem events
112
+
113
+ By default, when the `:fsevents` key in the `Vagrantfile` is configured with
114
+ `true`, all filesystem events are forwarded to the VM (creation, modification,
115
+ and removal). If, instead, you want to select only some of those events to be
116
+ forwarded (e.g. you don't care about file removals), you can use an Array of
117
+ Symbols among the following options: `:added`, `:modified` and `:removed`.
118
+
119
+ For example, to forward only added files events to the default `/vagrant`
120
+ folder, add the following to the `Vagrantfile`:
121
+
122
+ ```ruby
123
+ config.vm.synced_folder ".", "/vagrant", fsevents: [:added]
124
+ ```
125
+
126
+ ## Original work
127
+
128
+ This plugin was originally [`vagrant-fsnotify`][vagrant-fsnotify] by @adrienkohlbecker,
129
+ but was renamed to facilitate continued development.
130
+ This plugin used [`vagrant-rsync-back`][vagrant-rsync-back] by @smerill and the
131
+ [Vagrant][vagrant] source code as a starting point.
132
+
133
+ [vagrant]: https://www.vagrantup.com/
134
+ [virtualbox]: https://www.virtualbox.org/
135
+ [jekyll]: http://jekyllrb.com/
136
+ [guard]: http://guardgem.org/
137
+ [forwarding-file-events-over-tcp]: https://github.com/guard/listen#forwarding-file-events-over-tcp
138
+ [vagrant-bindfs]: https://github.com/gael-ian/vagrant-bindfs
139
+ [vagrant-rsync-back]: https://github.com/smerrill/vagrant-rsync-back
140
+ [vagrant-fsnotify]: https://github.com/adrienkohlbecker/vagrant-fsnotify
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/Vagrantfile ADDED
@@ -0,0 +1,122 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5
+ VAGRANTFILE_API_VERSION = "2"
6
+
7
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8
+ # All Vagrant configuration is done here. The most common configuration
9
+ # options are documented and commented below. For a complete reference,
10
+ # please see the online documentation at vagrantup.com.
11
+
12
+ # Every Vagrant virtual environment requires a box to build off of.
13
+ config.vm.box = "ubuntu/trusty64"
14
+
15
+ # Disable automatic box update checking. If you disable this, then
16
+ # boxes will only be checked for updates when the user runs
17
+ # `vagrant box outdated`. This is not recommended.
18
+ # config.vm.box_check_update = false
19
+
20
+ # Create a forwarded port mapping which allows access to a specific port
21
+ # within the machine from a port on the host machine. In the example below,
22
+ # accessing "localhost:8080" will access port 80 on the guest machine.
23
+ # config.vm.network "forwarded_port", guest: 80, host: 8080
24
+
25
+ # Create a private network, which allows host-only access to the machine
26
+ # using a specific IP.
27
+ config.vm.network "private_network", ip: "192.168.33.10"
28
+
29
+ # Create a public network, which generally matched to bridged network.
30
+ # Bridged networks make the machine appear as another physical device on
31
+ # your network.
32
+ # config.vm.network "public_network"
33
+
34
+ # If true, then any SSH connections made will enable agent forwarding.
35
+ # Default value: false
36
+ # config.ssh.forward_agent = true
37
+
38
+ # Share an additional folder to the guest VM. The first argument is
39
+ # the path on the host to the actual folder. The second argument is
40
+ # the path on the guest to mount the folder. And the optional third
41
+ # argument is a set of non-required options.
42
+ config.vm.synced_folder ".", "/vagrant", fsevents: true
43
+
44
+ # Provider-specific configuration so you can fine-tune various
45
+ # backing providers for Vagrant. These expose provider-specific options.
46
+ # Example for VirtualBox:
47
+ #
48
+ # config.vm.provider "virtualbox" do |vb|
49
+ # # Don't boot with headless mode
50
+ # vb.gui = true
51
+ #
52
+ # # Use VBoxManage to customize the VM. For example to change memory:
53
+ # vb.customize ["modifyvm", :id, "--memory", "1024"]
54
+ # end
55
+ #
56
+ # View the documentation for the provider you're using for more
57
+ # information on available options.
58
+
59
+ # Enable provisioning with CFEngine. CFEngine Community packages are
60
+ # automatically installed. For example, configure the host as a
61
+ # policy server and optionally a policy file to run:
62
+ #
63
+ # config.vm.provision "cfengine" do |cf|
64
+ # cf.am_policy_hub = true
65
+ # # cf.run_file = "motd.cf"
66
+ # end
67
+ #
68
+ # You can also configure and bootstrap a client to an existing
69
+ # policy server:
70
+ #
71
+ # config.vm.provision "cfengine" do |cf|
72
+ # cf.policy_server_address = "10.0.2.15"
73
+ # end
74
+
75
+ # Enable provisioning with Puppet stand alone. Puppet manifests
76
+ # are contained in a directory path relative to this Vagrantfile.
77
+ # You will need to create the manifests directory and a manifest in
78
+ # the file default.pp in the manifests_path directory.
79
+ #
80
+ # config.vm.provision "puppet" do |puppet|
81
+ # puppet.manifests_path = "manifests"
82
+ # puppet.manifest_file = "site.pp"
83
+ # end
84
+
85
+ # Enable provisioning with chef solo, specifying a cookbooks path, roles
86
+ # path, and data_bags path (all relative to this Vagrantfile), and adding
87
+ # some recipes and/or roles.
88
+ #
89
+ # config.vm.provision "chef_solo" do |chef|
90
+ # chef.cookbooks_path = "../my-recipes/cookbooks"
91
+ # chef.roles_path = "../my-recipes/roles"
92
+ # chef.data_bags_path = "../my-recipes/data_bags"
93
+ # chef.add_recipe "mysql"
94
+ # chef.add_role "web"
95
+ #
96
+ # # You may also specify custom JSON attributes:
97
+ # chef.json = { mysql_password: "foo" }
98
+ # end
99
+
100
+ # Enable provisioning with chef server, specifying the chef server URL,
101
+ # and the path to the validation key (relative to this Vagrantfile).
102
+ #
103
+ # The Opscode Platform uses HTTPS. Substitute your organization for
104
+ # ORGNAME in the URL and validation key.
105
+ #
106
+ # If you have your own Chef Server, use the appropriate URL, which may be
107
+ # HTTP instead of HTTPS depending on your configuration. Also change the
108
+ # validation key to validation.pem.
109
+ #
110
+ # config.vm.provision "chef_client" do |chef|
111
+ # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
112
+ # chef.validation_key_path = "ORGNAME-validator.pem"
113
+ # end
114
+ #
115
+ # If you're using the Opscode platform, your validator client is
116
+ # ORGNAME-validator, replacing ORGNAME with your organization name.
117
+ #
118
+ # If you have your own Chef Server, the default validation client name is
119
+ # chef-validator, unless you changed the configuration.
120
+ #
121
+ # chef.validation_client_name = "ORGNAME-validator"
122
+ end
@@ -0,0 +1,21 @@
1
+ module VagrantPlugins
2
+ module Fsevents
3
+ # Place to store messages that will be seen by the user
4
+ class Alerter
5
+ # Alert the user to a successful folder watch start
6
+ def watching(machine, rel_path)
7
+ machine.ui.info("fsevents: Watching #{rel_path}")
8
+ end
9
+
10
+ def event(event)
11
+ word = 'Changed' if event.type == :modified
12
+ word = 'Added' if event.type == :added
13
+ word = 'Removed' if event.type == :removed
14
+
15
+ event.watch[:machine].ui.info(
16
+ "fsevents: #{word}: #{event.relative_path}"
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'errors'
2
+
3
+ module VagrantPlugins
4
+ module Fsevents
5
+ # A single changed path string of a specified change type
6
+ class ChangeEvents
7
+ attr_reader :path
8
+ attr_reader :type
9
+ attr_reader :watch
10
+ attr_reader :watch_path
11
+ attr_reader :relative_path
12
+ attr_reader :full_path_on_guest
13
+
14
+ # Takes the output of a listen callback (as arrays of strings of
15
+ # files split by type of change) and converts them into single events
16
+ # for a specific watch definition
17
+ def self.make(mods, adds, removes, watch_path, watch)
18
+ change_events = []
19
+ structure = { modified: mods, added: adds, removed: removes }
20
+ structure.each do |type, files|
21
+ files.each do |path|
22
+ change_events.push new(path, type, watch_path, watch)
23
+ end
24
+ end
25
+ change_events
26
+ end
27
+
28
+ def initialize(path, type, watch_path, watch)
29
+ @path = path
30
+ @type = type
31
+ @watch_path = watch_path
32
+ @watch = watch
33
+
34
+ @relative_path = path.sub(watch_path, '')
35
+ @full_path_on_guest = File.join(
36
+ (watch[:opts][:override_guestpath] || watch[:opts][:guestpath]),
37
+ @relative_path
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ module VagrantPlugins
2
+ module Fsevents
3
+ # Error indicating that there is no valid path to watch for changes
4
+ class NothingToSyncError < Vagrant::Errors::VagrantError
5
+ error_message %q(
6
+ Nothing to sync.
7
+
8
+ Note that the valid values for the `:fsevents' configuration key on
9
+ `Vagrantfile' are either `true' (which forwards all kinds of filesystem
10
+ events) or an Array containing symbols among the following options:
11
+ `:modified', `:added' and `:removed' (in which case, only the specified
12
+ filesystem events are forwarded).
13
+
14
+ For example, to forward all filesystem events to the default `/vagrant'
15
+ folder, add the following to the `Vagrantfile':
16
+
17
+ config.vm.synced_folder ".", "/vagrant", fsevents: true
18
+
19
+ And to forward only added files events to the default `/vagrant' folder,
20
+ add the following to the `Vagrantfile':
21
+
22
+ config.vm.synced_folder ".", "/vagrant", fsevents: [:added]
23
+
24
+ Exiting...
25
+ ).gsub(/^[ ]{8}/, '')
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module Fsevents
3
+ # Methods that log to Vagrant's internal log for debugging
4
+ class Logger < Log4r::Logger
5
+ # Logs listening start messages
6
+ def listening_with_adapter(adapter, paths, ignores)
7
+ info("Listening to paths: #{paths.keys.sort.inspect}")
8
+ info("Listening via: #{adapter}")
9
+ info("Ignoring #{ignores.length} paths:")
10
+ ignores.each do |ignore|
11
+ info(" -- #{ignore}")
12
+ end
13
+ end
14
+
15
+ # Logs when the file change callback is triggered
16
+ def callback_start(mods, adds, removes)
17
+ info('File change callback called!')
18
+ info(" - Modified: #{mods.inspect}")
19
+ info(" - Added: #{adds.inspect}")
20
+ info(" - Removed: #{removes.inspect}")
21
+ end
22
+
23
+ # Logs when a path was changed too quickly and was ignored
24
+ def change_too_soon(rel_path)
25
+ info(
26
+ "Skipping #{rel_path} - was changed too recently"
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,99 @@
1
+ require 'listen'
2
+ require_relative 'errors'
3
+
4
+ module VagrantPlugins
5
+ module Fsevents
6
+ # Class containing the main plugin activities
7
+ class Main < Vagrant.plugin('2', :command)
8
+ include Vagrant::Action::Builtin::MixinSyncedFolders
9
+
10
+ LOGGING_CONTEXT = 'vagrant::commands::fsevents'.freeze
11
+ ADAPTER = Listen::Adapter.select.inspect.freeze
12
+
13
+ # Main method for constructing the plugin
14
+ def execute
15
+ init_supporting_classes
16
+ init_path_data
17
+ log_listening_start_event
18
+ print_watch_paths_to_console
19
+ start_listening
20
+ end
21
+
22
+ # Instantiate supporting classes
23
+ def init_supporting_classes
24
+ @logger = VagrantPlugins::Fsevents::Logger.new LOGGING_CONTEXT
25
+ @alerter = VagrantPlugins::Fsevents::Alerter.new
26
+ @responder = VagrantPlugins::Fsevents::Responder.new @logger, @alerter
27
+ @paths = VagrantPlugins::Fsevents::Paths.new
28
+ end
29
+
30
+ # Build list of paths to watch and ignore
31
+ def init_path_data
32
+ with_target_vms(command_line_argv) do |machine|
33
+ unless machine.communicate.ready?
34
+ raise Vagrant::Errors::VMNotCreatedError
35
+ end
36
+
37
+ @paths.process(machine, synced_folders(machine))
38
+ end
39
+
40
+ raise NothingToSyncError.new if @paths.watch.empty?
41
+ end
42
+
43
+ def self.synopsis
44
+ 'forwards filesystem events to virtual machine'
45
+ end
46
+
47
+ # Log that we've started listening
48
+ def log_listening_start_event
49
+ @logger.listening_with_adapter(ADAPTER, @paths.watch, @paths.ignore)
50
+ end
51
+
52
+ # Alert the user to the paths we're watching
53
+ def print_watch_paths_to_console
54
+ @paths.watch.each do |path, data|
55
+ @alerter.watching(data[:machine], path)
56
+ end
57
+ end
58
+
59
+ # Start the actual listener that will respond to file changes
60
+ def start_listening
61
+ listener = Listen.to(
62
+ *@paths.watch.keys,
63
+ ignore: @paths.ignore,
64
+ &@responder.callback_proc.curry[@paths.watch]
65
+ )
66
+
67
+ create_interrupt_callback(listener)
68
+ end
69
+
70
+ # Create the callback that lets us know when we've been interrupted
71
+ def create_interrupt_callback(listener)
72
+ queue = Queue.new
73
+ callback = lambda do
74
+ # This needs to execute in another thread because Thread
75
+ # synchronization can't happen in a trap context.
76
+ Thread.new { queue << true }
77
+ end
78
+
79
+ # Run the listener in a busy block so that we can cleanly
80
+ # exit once we receive an interrupt.
81
+ Vagrant::Util::Busy.busy(callback) do
82
+ listener.start
83
+ queue.pop
84
+ listener.stop if listener.state != :stopped
85
+ end
86
+
87
+ 0
88
+ end
89
+
90
+ # This parses the command line arguments passed by the user
91
+ def command_line_argv
92
+ parse_options OptionParser.new do |o|
93
+ o.banner = 'Usage: vagrant fsevents [vm-name]'
94
+ o.separator ''
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,74 @@
1
+ module VagrantPlugins
2
+ module Fsevents
3
+ # Logic for paths to watch and ignore
4
+ class Paths
5
+ attr_reader :watch
6
+ attr_reader :ignore
7
+
8
+ def initialize
9
+ @watch = {}
10
+ @ignore = []
11
+ end
12
+
13
+ def process(machine, folders)
14
+ add_folders_to_watch(machine, folders)
15
+ add_folders_to_ignore(folders)
16
+ end
17
+
18
+ # Populates `@watch` attribute from a single VM instance
19
+ def add_folders_to_watch(machine, folders)
20
+ folders.values.each do |folder|
21
+ folder.each do |id, opts|
22
+ next unless active_for_folder? opts[:fsevents]
23
+
24
+ hostpath = File.expand_path(opts[:hostpath], machine.env.root_path)
25
+ hostpath = Vagrant::Util::Platform.fs_real_path(hostpath).to_s
26
+
27
+ # Avoid creating a nested directory
28
+ hostpath += '/' unless hostpath.end_with?('/')
29
+
30
+ @watch[hostpath] = { id: id, machine: machine, opts: opts }
31
+ end
32
+ end
33
+ end
34
+
35
+ # Populates `@ignore` attribute from a single VM instance
36
+ def add_folders_to_ignore(folders)
37
+ folders.values.each do |folder|
38
+ folder.values.each do |opts|
39
+ next unless active_for_folder?(opts[:fsevents])
40
+
41
+ Array(opts[:exclude]).each do |pattern|
42
+ @ignore << exclude_to_regexp(pattern.to_s)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ # This is REALLY ghetto, but its a start. We can improve and
49
+ # keep unit tests passing in the future.
50
+ def exclude_to_regexp(exclude)
51
+ exclude = exclude.gsub('**', '|||GLOBAL|||')
52
+ exclude = exclude.gsub('*', '|||PATH|||')
53
+ exclude = exclude.gsub('|||PATH|||', '[^/]*')
54
+ exclude = exclude.gsub('|||GLOBAL|||', '.*')
55
+
56
+ Regexp.new(exclude)
57
+ end
58
+
59
+ # checks to ensure that a path watch is not completely disabled
60
+ def active_for_folder?(options)
61
+ (
62
+ (options == true) || (
63
+ options.respond_to?(:include?) &&
64
+ (
65
+ options.include?(:modified) ||
66
+ options.include?(:added) ||
67
+ options.include?(:removed)
68
+ )
69
+ )
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,27 @@
1
+ begin
2
+ require 'vagrant'
3
+ rescue LoadError
4
+ raise 'The vagrant-fsevents plugin must be run within Vagrant.'
5
+ end
6
+
7
+ if Vagrant::VERSION < '1.7.3'
8
+ raise <<-ERROR
9
+ The vagrant-fsevents plugin is only compatible with Vagrant 1.7.3+. If you can't
10
+ upgrade, consider installing an old version of vagrant-fsnotify with:
11
+ $ vagrant plugin install vagrant-fsnotify --plugin-version 0.0.6.
12
+ ERROR
13
+ end
14
+
15
+ module VagrantPlugins
16
+ module Fsevents
17
+ # Class to instantiate the plugin
18
+ class Plugin < Vagrant.plugin('2')
19
+ name 'vagrant-fsevents'
20
+
21
+ command 'fsevents' do
22
+ require_relative 'main'
23
+ Main
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,96 @@
1
+ require_relative 'changeEvents'
2
+
3
+ module VagrantPlugins
4
+ module Fsevents
5
+ # Defines how to respond to a change event
6
+ class Responder
7
+ def initialize(logger, alerter)
8
+ @logger = logger
9
+ @alerter = alerter
10
+ @recently_changed = Hash.new { |hash, key| hash[key] = 0 }
11
+ end
12
+
13
+ # This is the main callback that responds to a change detection event
14
+ def callback(watches, mods, adds, removes)
15
+ @logger.callback_start(mods, adds, removes)
16
+
17
+ clear_expired_change_locks
18
+
19
+ events_to_sync = watches.map do |watch_path, watch|
20
+ events = ChangeEvents.make(mods, adds, removes, watch_path, watch)
21
+ add_locks(remove_locked_events(select_by_relevance(events)))
22
+ end
23
+
24
+ forward_events_to_vms(events_to_sync.flatten)
25
+ rescue StandardError => e
26
+ @logger.error("#{e}: #{e.message}")
27
+ end
28
+
29
+ # Return the callback as a proc that can be passed to the listener
30
+ def callback_proc
31
+ method(:callback).to_proc
32
+ end
33
+
34
+ # Filters out events whose path or type don't match their watch
35
+ def select_by_relevance(events)
36
+ events.select do |event|
37
+ next unless event.path.start_with?(event.watch_path)
38
+
39
+ watch = event.watch[:opts][:fsevents]
40
+ (watch == true || watch.includes?(event.type))
41
+ end
42
+ end
43
+
44
+ # Filters out events still in their change deadzone
45
+ def remove_locked_events(events)
46
+ events.select do |event|
47
+ if @recently_changed[event.relative_path] >= Time.now.to_f
48
+ @logger.change_too_soon(event.relative_path)
49
+ next
50
+ end
51
+
52
+ true
53
+ end
54
+ end
55
+
56
+ # Adds deadzone locks for given events
57
+ def add_locks(events)
58
+ lock_duration = Time.now.to_f + 2 + (0.02 * events.length)
59
+ events.each do |event|
60
+ @recently_changed[event.relative_path] = lock_duration
61
+ @alerter.event(event)
62
+ end
63
+ events
64
+ end
65
+
66
+ def group_events_by_machine(events)
67
+ machine_hash = Hash.new { |hash, key| hash[key] = [] }
68
+ events.each { |event| machine_hash[event.watch[:machine]] << event }
69
+ machine_hash
70
+ end
71
+
72
+ # Action events that have been bound to a watch and passed checks
73
+ def forward_events_to_vms(ungrouped_events)
74
+ group_events_by_machine(ungrouped_events).each do |machine, events|
75
+ changed_files = events.map(&:full_path_on_guest)
76
+ delete_events = events.select { |event| event.type == :removed }
77
+ sync_command = "touch -a '#{changed_files.join("' '")}'"
78
+
79
+ unless delete_events.empty?
80
+ deleted_files = delete_events.map(&:full_path_on_guest)
81
+ sync_command += "; rm -rf '#{deleted_files.join("' '")}'"
82
+ end
83
+
84
+ machine.communicate.execute(sync_command)
85
+ end
86
+ end
87
+
88
+ # Clear any stored change events from runs older than two seconds
89
+ def clear_expired_change_locks
90
+ @recently_changed.each do |rel_path, time|
91
+ @recently_changed.delete(rel_path) if time < Time.now.to_f
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module Fsevents
3
+ VERSION = '0.4.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ require 'vagrant-fsevents/version'
2
+ require 'vagrant-fsevents/plugin'
3
+
4
+ module VagrantPlugins
5
+ # Plugin core module
6
+ module Fsevents
7
+ lib_path = Pathname.new(File.expand_path('vagrant-fsevents', __dir__))
8
+ autoload :Alerter, lib_path.join('alerter')
9
+ autoload :Logger, lib_path.join('logger')
10
+ autoload :Responder, lib_path.join('responder')
11
+ autoload :Paths, lib_path.join('paths')
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'vagrant-fsevents/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'vagrant-fsevents'
7
+ spec.version = VagrantPlugins::Fsevents::VERSION
8
+ spec.authors = ['Wolf Hatch']
9
+ spec.email = ['wolf@mechrus.com']
10
+ spec.summary = 'Forward filesystem change notifications '\
11
+ 'to your Vagrant VM'
12
+ spec.description = 'Use vagrant-fsevents to forward filesystem '\
13
+ 'change notifications to your Vagrant VM'
14
+ spec.homepage = 'https://github.com/Fendrian/vagrant-fsevents'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'rake'
26
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vagrant-fsevents
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ platform: ruby
6
+ authors:
7
+ - Wolf Hatch
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-03-07 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: Use vagrant-fsevents to forward filesystem change notifications to your
42
+ Vagrant VM
43
+ email:
44
+ - wolf@mechrus.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - ".rubocop.yml"
51
+ - AUTHORS
52
+ - CHANGELOG.md
53
+ - Gemfile
54
+ - LICENSE
55
+ - README.md
56
+ - Rakefile
57
+ - Vagrantfile
58
+ - lib/vagrant-fsevents.rb
59
+ - lib/vagrant-fsevents/alerter.rb
60
+ - lib/vagrant-fsevents/changeEvents.rb
61
+ - lib/vagrant-fsevents/errors.rb
62
+ - lib/vagrant-fsevents/logger.rb
63
+ - lib/vagrant-fsevents/main.rb
64
+ - lib/vagrant-fsevents/paths.rb
65
+ - lib/vagrant-fsevents/plugin.rb
66
+ - lib/vagrant-fsevents/responder.rb
67
+ - lib/vagrant-fsevents/version.rb
68
+ - vagrant-fsevents.gemspec
69
+ homepage: https://github.com/Fendrian/vagrant-fsevents
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.7.6
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Forward filesystem change notifications to your Vagrant VM
93
+ test_files: []