vagrant-fsevents 0.4.1

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