vagrant-unison-morroni 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +10 -0
- data/LICENSE +8 -0
- data/README.md +88 -0
- data/Rakefile +21 -0
- data/lib/vagrant-unison-morroni/command.rb +142 -0
- data/lib/vagrant-unison-morroni/config.rb +66 -0
- data/lib/vagrant-unison-morroni/errors.rb +9 -0
- data/lib/vagrant-unison-morroni/plugin.rb +103 -0
- data/lib/vagrant-unison-morroni/shell_command.rb +55 -0
- data/lib/vagrant-unison-morroni/ssh_command.rb +43 -0
- data/lib/vagrant-unison-morroni/unison_paths.rb +24 -0
- data/lib/vagrant-unison-morroni/unison_sync.rb +22 -0
- data/lib/vagrant-unison-morroni/version.rb +5 -0
- data/lib/vagrant-unison-morroni.rb +15 -0
- data/locales/en.yml +19 -0
- data/spec/vagrant-unison-morroni/config_spec.rb +32 -0
- data/vagrant-unison-morroni.gemspec +58 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ba44c81368f6e3599f6e949cac3facf725b5d302
|
4
|
+
data.tar.gz: f72fe8e35dff47ae0375db755245ab7e9e3fac49
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7c9e2115f11ad1ddca33335a8d4846e890f5ba09640bb41886203792a16564165b4fbea25f196702a80b9d5fcffb990e1ed6ec007c84edfff4a5dbf857169034
|
7
|
+
data.tar.gz: 584126d0f40b221d5bfdf0aa052aca95b0e8de28864c3a1b6fea0fcab1a2d8b8093f45a8c3e454f93dbe051232d853fe186d115673080721a38602087e85721a
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
# We depend on Vagrant for development, but we don't add it as a
|
7
|
+
# gem dependency because we expect to be installed within the
|
8
|
+
# Vagrant environment itself using `vagrant plugin`.
|
9
|
+
gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git"
|
10
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
Copyright (c) 2013 Mitchell Hashimoto
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
5
|
+
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
7
|
+
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Vagrant Unison Plugin
|
2
|
+
|
3
|
+
This is a [Vagrant](http://www.vagrantup.com) 1.1+ plugin that syncs files over SSH from a local folder
|
4
|
+
to your Vagrant VM (local or on AWS). Under the covers it uses [Unison](http://www.cis.upenn.edu/~bcpierce/unison/)
|
5
|
+
|
6
|
+
**NOTE:** This plugin requires Vagrant 1.1+,
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
* Unisoned folder support via `unison` over `ssh` -> will work with any vagrant provider, eg Virtualbox or AWS.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
1. You must already have [Unison](http://www.cis.upenn.edu/~bcpierce/unison/) installed and in your path.
|
15
|
+
* On Mac you can install this with Homebrew: `brew install unison` (on Yosemite you will have to use https://rudix-mountainlion.googlecode.com/files/unison-2.40.102-0.pkg)
|
16
|
+
* On Unix (Ubuntu) install using `sudo apt-get install unison`
|
17
|
+
* On Windows, download [2.40.102](http://alan.petitepomme.net/unison/assets/Unison-2.40.102.zip), unzip, rename `Unison-2.40.102 Text.exe` to `unison.exe` and copy to somewhere in your path.
|
18
|
+
1. Install using standard Vagrant 1.1+ plugin installation methods.
|
19
|
+
```
|
20
|
+
$ vagrant plugin install vagrant-unison
|
21
|
+
```
|
22
|
+
1. After installing, edit your Vagrantfile and add a configuration directive similar to the below:
|
23
|
+
```
|
24
|
+
Vagrant.configure("2") do |config|
|
25
|
+
config.vm.box = "dummy"
|
26
|
+
|
27
|
+
config.sync.host_folder = "src/" #relative to the folder your Vagrantfile is in
|
28
|
+
config.sync.guest_folder = "src/" #relative to the vagrant home folder -> /home/vagrant
|
29
|
+
config.sync.ignore = "Name {.idea,.DS_Store}"
|
30
|
+
|
31
|
+
end
|
32
|
+
```
|
33
|
+
1. Start up your starting your vagrant box as normal (eg: `vagrant up`)
|
34
|
+
|
35
|
+
## Start syncing Folders
|
36
|
+
|
37
|
+
Run `vagrant sync` to start watching the local_folder for changes, and syncing these to your vagrang VM.
|
38
|
+
|
39
|
+
Under the covers this uses your system installation of [Unison](http://www.cis.upenn.edu/~bcpierce/unison/),
|
40
|
+
which must be installed in your path.
|
41
|
+
|
42
|
+
## Start syncing Folders in repeat mode
|
43
|
+
|
44
|
+
Run `vagrant sync-repeat` to start in bidirect monitor (repeat) mode.
|
45
|
+
|
46
|
+
## Start syncing Folders in interactive mode
|
47
|
+
|
48
|
+
Run `vagrant sync-interact` to start in interactive mode that allows solving conflicts.
|
49
|
+
|
50
|
+
## Cleanup unison database
|
51
|
+
When you get
|
52
|
+
```
|
53
|
+
Fatal error: Warning: inconsistent state.
|
54
|
+
The archive file is missing on some hosts.
|
55
|
+
For safety, the remaining copies should be deleted.
|
56
|
+
Archive arb126d8de1ef26a835b94cf51975c530f on host blablabla.local should be DELETED
|
57
|
+
Archive arbc6a36f85b3d1473c55565dd220acf68 on host blablabla is MISSING
|
58
|
+
Please delete archive files as appropriate and try again
|
59
|
+
or invoke Unison with -ignorearchives flag.
|
60
|
+
```
|
61
|
+
|
62
|
+
Run `vagrant sync-cleanup` to clear Archive from ~/Library/Application Support/Unison/ and files from host folder.
|
63
|
+
Running Unison with -ignorearchives flag is a bad idea, since it will produce conflicts.
|
64
|
+
|
65
|
+
## Development
|
66
|
+
|
67
|
+
To work on the `vagrant-unison` plugin, clone this repository out, and use
|
68
|
+
[Bundler](http://gembundler.com) to get the dependencies:
|
69
|
+
|
70
|
+
```
|
71
|
+
$ bundle
|
72
|
+
```
|
73
|
+
|
74
|
+
Once you have the dependencies, verify the unit tests pass with `rake`:
|
75
|
+
|
76
|
+
```
|
77
|
+
$ bundle exec rake
|
78
|
+
```
|
79
|
+
|
80
|
+
If those pass, you're ready to start developing the plugin. You can test
|
81
|
+
the plugin without installing it into your Vagrant environment by just
|
82
|
+
creating a `Vagrantfile` in the top level of this directory (it is gitignored)
|
83
|
+
that uses it, and uses bundler to execute Vagrant:
|
84
|
+
|
85
|
+
```
|
86
|
+
$ bundle exec vagrant up
|
87
|
+
$ bundle exec vagrant sync
|
88
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
# Immediately sync all stdout so that tools like buildbot can
|
6
|
+
# immediately load in the output.
|
7
|
+
$stdout.sync = true
|
8
|
+
$stderr.sync = true
|
9
|
+
|
10
|
+
# Change to the directory of this file.
|
11
|
+
Dir.chdir(File.expand_path("../", __FILE__))
|
12
|
+
|
13
|
+
# This installs the tasks that help with gem creation and
|
14
|
+
# publishing.
|
15
|
+
Bundler::GemHelper.install_tasks
|
16
|
+
|
17
|
+
# Install the `spec` task so that we can run tests.
|
18
|
+
RSpec::Core::RakeTask.new
|
19
|
+
|
20
|
+
# Default task is to run the unit tests
|
21
|
+
task :default => "spec"
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require "vagrant"
|
3
|
+
require "thread"
|
4
|
+
require 'listen'
|
5
|
+
|
6
|
+
require_relative 'unison_paths'
|
7
|
+
require_relative 'ssh_command'
|
8
|
+
require_relative 'shell_command'
|
9
|
+
require_relative 'unison_sync'
|
10
|
+
|
11
|
+
module VagrantPlugins
|
12
|
+
module Unison
|
13
|
+
class Command < Vagrant.plugin("2", :command)
|
14
|
+
include UnisonSync
|
15
|
+
|
16
|
+
def execute
|
17
|
+
with_target_vms do |machine|
|
18
|
+
paths = UnisonPaths.new(@env, machine)
|
19
|
+
host_path = paths.host
|
20
|
+
|
21
|
+
sync(machine, paths)
|
22
|
+
|
23
|
+
@env.ui.info "Watching #{host_path} for changes..."
|
24
|
+
|
25
|
+
listener = Listen.to(host_path) do |modified, added, removed|
|
26
|
+
@env.ui.info "Detected modifications to #{modified.inspect}" unless modified.empty?
|
27
|
+
@env.ui.info "Detected new files #{added.inspect}" unless added.empty?
|
28
|
+
@env.ui.info "Detected deleted files #{removed.inspect}" unless removed.empty?
|
29
|
+
|
30
|
+
sync(machine, paths)
|
31
|
+
end
|
32
|
+
|
33
|
+
queue = Queue.new
|
34
|
+
|
35
|
+
callback = lambda do
|
36
|
+
# This needs to execute in another thread because Thread
|
37
|
+
# synchronization can't happen in a trap context.
|
38
|
+
Thread.new { queue << true }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Run the listener in a busy block so that we can cleanly
|
42
|
+
# exit once we receive an interrupt.
|
43
|
+
Vagrant::Util::Busy.busy(callback) do
|
44
|
+
listener.start
|
45
|
+
queue.pop
|
46
|
+
listener.stop if listener.listen?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
0
|
51
|
+
end
|
52
|
+
|
53
|
+
def sync(machine, paths)
|
54
|
+
execute_sync_command(machine) do |command|
|
55
|
+
command.batch = true
|
56
|
+
|
57
|
+
@env.ui.info "Running #{command.to_s}"
|
58
|
+
|
59
|
+
r = Vagrant::Util::Subprocess.execute(*command.to_a)
|
60
|
+
|
61
|
+
case r.exit_code
|
62
|
+
when 0
|
63
|
+
@env.ui.info "Unison completed succesfully"
|
64
|
+
when 1
|
65
|
+
@env.ui.info "Unison completed - all file transfers were successful; some files were skipped"
|
66
|
+
when 2
|
67
|
+
@env.ui.info "Unison completed - non-fatal failures during file transfer: #{r.stderr}"
|
68
|
+
else
|
69
|
+
raise Vagrant::Errors::UnisonError,
|
70
|
+
:command => command.to_s,
|
71
|
+
:guestpath => paths.guest,
|
72
|
+
:hostpath => paths.host,
|
73
|
+
:stderr => r.stderr
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class CommandRepeat < Vagrant.plugin("2", :command)
|
80
|
+
include UnisonSync
|
81
|
+
|
82
|
+
def execute
|
83
|
+
with_target_vms do |machine|
|
84
|
+
execute_sync_command(machine) do |command|
|
85
|
+
command.repeat = true
|
86
|
+
command.terse = true
|
87
|
+
command = command.to_s
|
88
|
+
|
89
|
+
@env.ui.info "Running #{command}"
|
90
|
+
|
91
|
+
system(command)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
0
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class CommandCleanup < Vagrant.plugin("2", :command)
|
100
|
+
include UnisonSync
|
101
|
+
|
102
|
+
def execute
|
103
|
+
with_target_vms do |machine|
|
104
|
+
guest_path = UnisonPaths.new(@env, machine).guest
|
105
|
+
|
106
|
+
command = "rm -rf ~/Library/'Application Support'/Unison/*"
|
107
|
+
@env.ui.info "Running #{command} on host"
|
108
|
+
system(command)
|
109
|
+
|
110
|
+
command = "rm -rf #{guest_path}"
|
111
|
+
@env.ui.info "Running #{command} on guest VM"
|
112
|
+
machine.communicate.sudo(command)
|
113
|
+
|
114
|
+
command = "rm -rf ~/.unison"
|
115
|
+
@env.ui.info "Running #{command} on guest VM"
|
116
|
+
machine.communicate.sudo(command)
|
117
|
+
end
|
118
|
+
|
119
|
+
0
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class CommandInteract < Vagrant.plugin("2", :command)
|
124
|
+
include UnisonSync
|
125
|
+
|
126
|
+
def execute
|
127
|
+
with_target_vms do |machine|
|
128
|
+
execute_sync_command(machine) do |command|
|
129
|
+
command.terse = true
|
130
|
+
command = command.to_s
|
131
|
+
|
132
|
+
@env.ui.info "Running #{command}"
|
133
|
+
|
134
|
+
system(command)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
0
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Unison
|
5
|
+
class Config < Vagrant.plugin("2", :config)
|
6
|
+
# Host Folder to Sync
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :host_folder
|
10
|
+
|
11
|
+
# Guest Folder to Sync.
|
12
|
+
#
|
13
|
+
# @return [String]
|
14
|
+
attr_accessor :guest_folder
|
15
|
+
|
16
|
+
# Pattern of files to ignore.
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
attr_accessor :ignore
|
20
|
+
|
21
|
+
# Repeat speed.
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
attr_accessor :repeat
|
25
|
+
|
26
|
+
def initialize(region_specific=false)
|
27
|
+
@host_folder = UNSET_VALUE
|
28
|
+
@remote_folder = UNSET_VALUE
|
29
|
+
@ignore = UNSET_VALUE
|
30
|
+
@repeat = UNSET_VALUE
|
31
|
+
end
|
32
|
+
|
33
|
+
#-------------------------------------------------------------------
|
34
|
+
# Internal methods.
|
35
|
+
#-------------------------------------------------------------------
|
36
|
+
|
37
|
+
# def merge(other)
|
38
|
+
# super.tap do |result|
|
39
|
+
# # TODO - do something sensible; current last config wins
|
40
|
+
# result.local_folder = other.local_folder
|
41
|
+
# result.remote_folder = other.remote_folder
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
|
45
|
+
def finalize!
|
46
|
+
# The access keys default to nil
|
47
|
+
@host_folder = nil if @host_folder == UNSET_VALUE
|
48
|
+
@guest_folder = nil if @guest_folder == UNSET_VALUE
|
49
|
+
@ignore = nil if @ignore == UNSET_VALUE
|
50
|
+
@repeat = 1 if @repeat == UNSET_VALUE
|
51
|
+
|
52
|
+
# Mark that we finalized
|
53
|
+
@__finalized = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate(machine)
|
57
|
+
errors = []
|
58
|
+
|
59
|
+
errors << I18n.t("vagrant_sync.config.host_folder_required") if @host_folder.nil?
|
60
|
+
errors << I18n.t("vagrant_sync.config.guest_folder_required") if @guest_folder.nil?
|
61
|
+
|
62
|
+
{ "Unison" => errors }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
begin
|
2
|
+
require "vagrant"
|
3
|
+
rescue LoadError
|
4
|
+
raise "The vagrant-unison plugin must be run within Vagrant."
|
5
|
+
end
|
6
|
+
|
7
|
+
# This is a sanity check to make sure no one is attempting to install
|
8
|
+
# this into an early Vagrant version.
|
9
|
+
if Vagrant::VERSION < "1.1.0"
|
10
|
+
raise "The vagrant-unison plugin is only compatible with Vagrant 1.1+"
|
11
|
+
end
|
12
|
+
|
13
|
+
module VagrantPlugins
|
14
|
+
module Unison
|
15
|
+
class Plugin < Vagrant.plugin("2")
|
16
|
+
name "Unison"
|
17
|
+
description <<-DESC
|
18
|
+
This plugin syncs files over SSH from a local folder
|
19
|
+
to your Vagrant VM (local or on AWS).
|
20
|
+
DESC
|
21
|
+
|
22
|
+
config "sync" do
|
23
|
+
require_relative "config"
|
24
|
+
Config
|
25
|
+
end
|
26
|
+
|
27
|
+
command "sync" do
|
28
|
+
# Setup logging and i18n
|
29
|
+
setup_logging
|
30
|
+
setup_i18n
|
31
|
+
|
32
|
+
#Return the command
|
33
|
+
require_relative "command"
|
34
|
+
Command
|
35
|
+
end
|
36
|
+
|
37
|
+
command "sync-repeat" do
|
38
|
+
# Setup logging and i18n
|
39
|
+
setup_logging
|
40
|
+
setup_i18n
|
41
|
+
|
42
|
+
#Return the command
|
43
|
+
require_relative "command"
|
44
|
+
CommandRepeat
|
45
|
+
end
|
46
|
+
|
47
|
+
command "sync-cleanup" do
|
48
|
+
# Setup logging and i18n
|
49
|
+
setup_logging
|
50
|
+
setup_i18n
|
51
|
+
|
52
|
+
#Return the command
|
53
|
+
require_relative "command"
|
54
|
+
CommandCleanup
|
55
|
+
end
|
56
|
+
|
57
|
+
command "sync-interact" do
|
58
|
+
# Setup logging and i18n
|
59
|
+
setup_logging
|
60
|
+
setup_i18n
|
61
|
+
|
62
|
+
#Return the command
|
63
|
+
require_relative "command"
|
64
|
+
CommandInteract
|
65
|
+
end
|
66
|
+
|
67
|
+
# This initializes the internationalization strings.
|
68
|
+
def self.setup_i18n
|
69
|
+
I18n.load_path << File.expand_path("locales/en.yml", Unison.source_root)
|
70
|
+
I18n.reload!
|
71
|
+
end
|
72
|
+
|
73
|
+
# This sets up our log level to be whatever VAGRANT_LOG is.
|
74
|
+
def self.setup_logging
|
75
|
+
require "log4r"
|
76
|
+
|
77
|
+
level = nil
|
78
|
+
begin
|
79
|
+
level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
|
80
|
+
rescue NameError
|
81
|
+
# This means that the logging constant wasn't found,
|
82
|
+
# which is fine. We just keep `level` as `nil`. But
|
83
|
+
# we tell the user.
|
84
|
+
level = nil
|
85
|
+
end
|
86
|
+
|
87
|
+
# Some constants, such as "true" resolve to booleans, so the
|
88
|
+
# above error checking doesn't catch it. This will check to make
|
89
|
+
# sure that the log level is an integer, as Log4r requires.
|
90
|
+
level = nil if !level.is_a?(Integer)
|
91
|
+
|
92
|
+
# Set the logging level on all "vagrant" namespaced
|
93
|
+
# logs as long as we have a valid level.
|
94
|
+
if level
|
95
|
+
logger = Log4r::Logger.new("vagrant_sync")
|
96
|
+
logger.outputters = Log4r::Outputter.stderr
|
97
|
+
logger.level = level
|
98
|
+
logger = nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Unison
|
3
|
+
class ShellCommand
|
4
|
+
def initialize machine, paths, ssh_command
|
5
|
+
@machine = machine
|
6
|
+
@paths = paths
|
7
|
+
@ssh_command = ssh_command
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :batch, :repeat, :terse
|
11
|
+
|
12
|
+
def to_a
|
13
|
+
args.map do |arg|
|
14
|
+
arg = arg[1...-1] if arg =~ /\A"(.*)"\z/
|
15
|
+
arg
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
args.join(' ')
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def args
|
26
|
+
[
|
27
|
+
'unison',
|
28
|
+
@paths.host,
|
29
|
+
@ssh_command.uri,
|
30
|
+
batch_arg,
|
31
|
+
terse_arg,
|
32
|
+
repeat_arg,
|
33
|
+
ignore_arg,
|
34
|
+
['-sshargs', %("#{@ssh_command.command}")],
|
35
|
+
].flatten.compact
|
36
|
+
end
|
37
|
+
|
38
|
+
def batch_arg
|
39
|
+
'-batch' if batch
|
40
|
+
end
|
41
|
+
|
42
|
+
def ignore_arg
|
43
|
+
['-ignore', %("#{@machine.config.sync.ignore}")] if @machine.config.sync.ignore
|
44
|
+
end
|
45
|
+
|
46
|
+
def repeat_arg
|
47
|
+
['-repeat', @machine.config.sync.repeat] if repeat && @machine.config.sync.repeat
|
48
|
+
end
|
49
|
+
|
50
|
+
def terse_arg
|
51
|
+
'-terse' if terse
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Unison
|
3
|
+
class SshCommand
|
4
|
+
def initialize(machine, unison_paths)
|
5
|
+
@machine = machine
|
6
|
+
@unison_paths = unison_paths
|
7
|
+
end
|
8
|
+
|
9
|
+
def command
|
10
|
+
%W(
|
11
|
+
-p #{ssh_info[:port]}
|
12
|
+
#{proxy_command}
|
13
|
+
-o StrictHostKeyChecking=no
|
14
|
+
-o UserKnownHostsFile=/dev/null
|
15
|
+
#{key_paths}
|
16
|
+
).compact.join(' ')
|
17
|
+
end
|
18
|
+
|
19
|
+
def uri
|
20
|
+
username = ssh_info[:username]
|
21
|
+
host = ssh_info[:host]
|
22
|
+
|
23
|
+
"ssh://#{username}@#{host}/#{@unison_paths.guest}"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def proxy_command
|
29
|
+
command = ssh_info[:proxy_command]
|
30
|
+
return nil unless command
|
31
|
+
"-o ProxyCommand='#{command}'"
|
32
|
+
end
|
33
|
+
|
34
|
+
def ssh_info
|
35
|
+
@machine.ssh_info
|
36
|
+
end
|
37
|
+
|
38
|
+
def key_paths
|
39
|
+
ssh_info[:private_key_path].map { |p| "-i #{p}" }.join(' ')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Unison
|
3
|
+
class UnisonPaths
|
4
|
+
def initialize(env, machine)
|
5
|
+
@env = env
|
6
|
+
@machine = machine
|
7
|
+
end
|
8
|
+
|
9
|
+
def guest
|
10
|
+
@machine.config.sync.guest_folder
|
11
|
+
end
|
12
|
+
|
13
|
+
def host
|
14
|
+
@host ||= begin
|
15
|
+
path = File.expand_path(@machine.config.sync.host_folder, @env.root_path)
|
16
|
+
|
17
|
+
# Make sure there is a trailing slash on the host path to
|
18
|
+
# avoid creating an additional directory with rsync
|
19
|
+
path = "#{path}/" if path !~ /\/$/
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Unison
|
3
|
+
module UnisonSync
|
4
|
+
def execute_sync_command(machine)
|
5
|
+
unison_paths = UnisonPaths.new(@env, machine)
|
6
|
+
guest_path = unison_paths.guest
|
7
|
+
host_path = unison_paths.host
|
8
|
+
|
9
|
+
@env.ui.info "Unisoning changes from {host}::#{host_path} --> {guest VM}::#{guest_path}"
|
10
|
+
|
11
|
+
# Create the guest path
|
12
|
+
machine.communicate.sudo("mkdir -p '#{guest_path}'")
|
13
|
+
machine.communicate.sudo("chown #{machine.ssh_info[:username]} '#{guest_path}'")
|
14
|
+
|
15
|
+
ssh_command = SshCommand.new(machine, unison_paths)
|
16
|
+
shell_command = ShellCommand.new(machine, unison_paths, ssh_command)
|
17
|
+
|
18
|
+
yield shell_command
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require "vagrant-unison-morroni/plugin"
|
4
|
+
require "vagrant-unison-morroni/errors"
|
5
|
+
|
6
|
+
module VagrantPlugins
|
7
|
+
module Unison
|
8
|
+
# This returns the path to the source of this plugin.
|
9
|
+
#
|
10
|
+
# @return [Pathname]
|
11
|
+
def self.source_root
|
12
|
+
@source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/locales/en.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
en:
|
2
|
+
vagrant_unison:
|
3
|
+
|
4
|
+
config:
|
5
|
+
host_folder_required: |-
|
6
|
+
Host folder is required
|
7
|
+
guest_folder_required: |-
|
8
|
+
Guest folder is required
|
9
|
+
|
10
|
+
errors:
|
11
|
+
unison_error: |-
|
12
|
+
There was an error when attemping to sync folders using unison.
|
13
|
+
Please inspect the error message below for more info.
|
14
|
+
|
15
|
+
Host path: %{hostpath}
|
16
|
+
Guest path: %{guestpath}
|
17
|
+
Error: %{stderr}
|
18
|
+
Full command causing error:
|
19
|
+
%{command}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "vagrant-unison-morroni/config"
|
2
|
+
|
3
|
+
describe VagrantPlugins::Unison::Config do
|
4
|
+
let(:instance) { described_class.new }
|
5
|
+
|
6
|
+
describe "defaults" do
|
7
|
+
subject do
|
8
|
+
instance.tap do |o|
|
9
|
+
o.finalize!
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
its("host_folder") { should be_nil }
|
14
|
+
its("guest_folder") { should be_nil }
|
15
|
+
its("ignore") { should be_nil }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "overriding defaults" do
|
19
|
+
# I typically don't meta-program in tests, but this is a very
|
20
|
+
# simple boilerplate test, so I cut corners here. It just sets
|
21
|
+
# each of these attributes to "foo" in isolation, and reads the value
|
22
|
+
# and asserts the proper result comes back out.
|
23
|
+
[:host_folder, :guest_folder].each do |attribute|
|
24
|
+
|
25
|
+
it "should not default #{attribute} if overridden" do
|
26
|
+
instance.send("#{attribute}=".to_sym, "foo")
|
27
|
+
instance.finalize!
|
28
|
+
instance.send(attribute).should == "foo"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
require "vagrant-unison-morroni/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "vagrant-unison-morroni"
|
6
|
+
s.version = VagrantPlugins::Unison::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = "David Laing"
|
9
|
+
s.email = "david@davidlaing.com"
|
10
|
+
s.homepage = "http://github.com/mrdavidlaing/vagrant-unison"
|
11
|
+
s.summary = "Vagrant 1.1 plugin to sync local files to VM over SSH"
|
12
|
+
s.description = "Vagrant 1.1 plugin to sync local files to VM over SSH using Unison"
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
|
16
|
+
s.add_runtime_dependency "listen", ">= 0.7.3"
|
17
|
+
s.add_runtime_dependency "rb-fsevent", "~> 0.9"
|
18
|
+
|
19
|
+
s.add_development_dependency "rake"
|
20
|
+
s.add_development_dependency "rspec-core", "~> 2.12.2"
|
21
|
+
s.add_development_dependency "rspec-expectations", "~> 2.12.1"
|
22
|
+
s.add_development_dependency "rspec-mocks", "~> 2.12.1"
|
23
|
+
|
24
|
+
# The following block of code determines the files that should be included
|
25
|
+
# in the gem. It does this by reading all the files in the directory where
|
26
|
+
# this gemspec is, and parsing out the ignored files from the gitignore.
|
27
|
+
# Note that the entire gitignore(5) syntax is not supported, specifically
|
28
|
+
# the "!" syntax, but it should mostly work correctly.
|
29
|
+
root_path = File.dirname(__FILE__)
|
30
|
+
all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") }
|
31
|
+
all_files.reject! { |file| [".", ".."].include?(File.basename(file)) }
|
32
|
+
gitignore_path = File.join(root_path, ".gitignore")
|
33
|
+
gitignore = File.readlines(gitignore_path)
|
34
|
+
gitignore.map! { |line| line.chomp.strip }
|
35
|
+
gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ }
|
36
|
+
|
37
|
+
unignored_files = all_files.reject do |file|
|
38
|
+
# Ignore any directories, the gemspec only cares about files
|
39
|
+
next true if File.directory?(file)
|
40
|
+
|
41
|
+
# Ignore any paths that match anything in the gitignore. We do
|
42
|
+
# two tests here:
|
43
|
+
#
|
44
|
+
# - First, test to see if the entire path matches the gitignore.
|
45
|
+
# - Second, match if the basename does, this makes it so that things
|
46
|
+
# like '.DS_Store' will match sub-directories too (same behavior
|
47
|
+
# as git).
|
48
|
+
#
|
49
|
+
gitignore.any? do |ignore|
|
50
|
+
File.fnmatch(ignore, file, File::FNM_PATHNAME) ||
|
51
|
+
File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
s.files = unignored_files
|
56
|
+
s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact
|
57
|
+
s.require_path = 'lib'
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vagrant-unison-morroni
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.16
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Laing
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: listen
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rb-fsevent
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-core
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.12.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.12.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-expectations
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.12.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.12.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-mocks
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.12.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.12.1
|
97
|
+
description: Vagrant 1.1 plugin to sync local files to VM over SSH using Unison
|
98
|
+
email: david@davidlaing.com
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- ".gitignore"
|
104
|
+
- CHANGELOG.md
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/vagrant-unison-morroni.rb
|
110
|
+
- lib/vagrant-unison-morroni/command.rb
|
111
|
+
- lib/vagrant-unison-morroni/config.rb
|
112
|
+
- lib/vagrant-unison-morroni/errors.rb
|
113
|
+
- lib/vagrant-unison-morroni/plugin.rb
|
114
|
+
- lib/vagrant-unison-morroni/shell_command.rb
|
115
|
+
- lib/vagrant-unison-morroni/ssh_command.rb
|
116
|
+
- lib/vagrant-unison-morroni/unison_paths.rb
|
117
|
+
- lib/vagrant-unison-morroni/unison_sync.rb
|
118
|
+
- lib/vagrant-unison-morroni/version.rb
|
119
|
+
- locales/en.yml
|
120
|
+
- spec/vagrant-unison-morroni/config_spec.rb
|
121
|
+
- vagrant-unison-morroni.gemspec
|
122
|
+
homepage: http://github.com/mrdavidlaing/vagrant-unison
|
123
|
+
licenses: []
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.3.6
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 2.2.2
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: Vagrant 1.1 plugin to sync local files to VM over SSH
|
145
|
+
test_files: []
|