vagrant-triggers 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +14 -0
- data/README.md +12 -6
- data/lib/vagrant-triggers/action/trigger.rb +34 -12
- data/lib/vagrant-triggers/config.rb +12 -4
- data/lib/vagrant-triggers/errors.rb +4 -0
- data/lib/vagrant-triggers/plugin.rb +4 -2
- data/lib/vagrant-triggers/version.rb +1 -1
- data/locales/en.yml +5 -1
- data/spec/vagrant-triggers/action/trigger_spec.rb +84 -44
- data/spec/vagrant-triggers/config_spec.rb +5 -0
- metadata +5 -4
data/CHANGELOG.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
## 0.2.0 (October 19, 2013)
|
2
|
+
|
3
|
+
NEW FEATURES:
|
4
|
+
|
5
|
+
- New option: ```:append_to_path```.
|
6
|
+
|
7
|
+
IMPROVEMENTS:
|
8
|
+
|
9
|
+
- Triggers won't run if ```VAGRANT_NO_TRIGGERS``` environment variable is set.
|
10
|
+
- Command argument could also be an array.
|
11
|
+
|
12
|
+
## 0.1.0 (August 19, 2013)
|
13
|
+
|
14
|
+
- Initial release.
|
data/README.md
CHANGED
@@ -18,19 +18,26 @@ Installation is performed in the prescribed manner for Vagrant 1.1+ plugins.
|
|
18
18
|
The following ```Vagrantfile``` configuration options are added:
|
19
19
|
|
20
20
|
```
|
21
|
-
|
21
|
+
trigger.before :command, { :option => "value", ... }
|
22
22
|
```
|
23
23
|
|
24
24
|
```
|
25
|
-
|
25
|
+
trigger.after :command, { :option => "value", ... }
|
26
26
|
```
|
27
27
|
|
28
|
+
The first argument is the command in which the trigger will be tied. It could be an array (e.g. ```[:up, :resume]```) in case of multiple commands.
|
29
|
+
|
28
30
|
### Options
|
29
31
|
|
30
32
|
* ```:execute => "script"```: the script to execute
|
33
|
+
* ```:append_to_path => ["dir", "dir"]```: additional places where looking for the script. See [this wiki page](https://github.com/emyl/vagrant-triggers/wiki/The-:append_to_path-option) for details.
|
31
34
|
* ```:force => true```: continue even if the script fails (exits with non-zero code)
|
32
35
|
* ```:stdout => true```: display script output
|
33
36
|
|
37
|
+
### Skipping execution
|
38
|
+
|
39
|
+
Triggers won't run if ```VAGRANT_NO_TRIGGERS``` environment variable is set.
|
40
|
+
|
34
41
|
## Example
|
35
42
|
|
36
43
|
In the following example a VirtualBox VM (not managed by Vagrant) will be tied to the machine defined in ```Vagrantfile```, to make so that it follows its lifecycle:
|
@@ -40,10 +47,9 @@ In the following example a VirtualBox VM (not managed by Vagrant) will be tied t
|
|
40
47
|
Vagrant.configure("2") do |config|
|
41
48
|
|
42
49
|
{
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:up => "startvm 22aed8b3-d246-40d5-8ad4-176c17552c43 --type headless"
|
50
|
+
[:up, :resume] => "startvm 22aed8b3-d246-40d5-8ad4-176c17552c43 --type headless",
|
51
|
+
:suspend => "controlvm 22aed8b3-d246-40d5-8ad4-176c17552c43 savestate",
|
52
|
+
:halt => "controlvm 22aed8b3-d246-40d5-8ad4-176c17552c43 acpipowerbutton",
|
47
53
|
}.each do |command, trigger|
|
48
54
|
config.trigger.before command, :execute => "vboxmanage #{trigger}", :stdout => true
|
49
55
|
end
|
@@ -20,6 +20,37 @@ module VagrantPlugins
|
|
20
20
|
@app.call(env)
|
21
21
|
end
|
22
22
|
|
23
|
+
def execute(raw_command)
|
24
|
+
@env[:ui].info 'Executing command "' + raw_command + '"...'
|
25
|
+
command = Shellwords.shellsplit(raw_command)
|
26
|
+
env_backup = ENV.to_hash
|
27
|
+
begin
|
28
|
+
result = Vagrant::Util::Subprocess.execute(command[0], *command[1..-1])
|
29
|
+
rescue Vagrant::Errors::CommandUnavailable, Vagrant::Errors::CommandUnavailableWindows
|
30
|
+
@logger.debug("Command not found in the path.")
|
31
|
+
@logger.debug("Current PATH: #{ENV["PATH"]}")
|
32
|
+
new_path = Array(@options[:append_to_path]).join(File::PATH_SEPARATOR)
|
33
|
+
@logger.debug("Temporary replacing the system PATH with #{new_path}.")
|
34
|
+
unless new_path.empty?
|
35
|
+
new_env = env_backup.dup
|
36
|
+
new_env.delete("PATH")
|
37
|
+
new_env["PATH"] = new_path
|
38
|
+
ENV.replace(new_env)
|
39
|
+
@options[:append_to_path] = nil
|
40
|
+
retry
|
41
|
+
end
|
42
|
+
raise Errors::CommandUnavailable, :command => command[0]
|
43
|
+
ensure
|
44
|
+
ENV.replace(env_backup)
|
45
|
+
end
|
46
|
+
if result.exit_code != 0 && !@options[:force]
|
47
|
+
raise Errors::CommandFailed, :command => raw_command, :stderr => result.stderr
|
48
|
+
end
|
49
|
+
if @options[:stdout]
|
50
|
+
@env[:ui].info "Command output:\n\n#{result.stdout}\n"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
23
54
|
def fire_triggers
|
24
55
|
# Triggers don't fire on environment load and unload.
|
25
56
|
return if [:environment_load, :environment_unload].include?(@env[:action_name])
|
@@ -31,18 +62,9 @@ module VagrantPlugins
|
|
31
62
|
unless triggers_to_fire.empty?
|
32
63
|
@env[:ui].info "Running triggers #{@condition} action..."
|
33
64
|
triggers_to_fire.each do |trigger|
|
34
|
-
options = trigger[:options]
|
35
|
-
if options[:execute]
|
36
|
-
|
37
|
-
@env[:ui].info 'Executing command "' + raw_command + '"...'
|
38
|
-
command = Shellwords.shellsplit(raw_command)
|
39
|
-
result = Vagrant::Util::Subprocess.execute(command[0], *command[1..-1])
|
40
|
-
if result.exit_code != 0 && !options[:force]
|
41
|
-
raise Errors::CommandFailed, :command => raw_command, :stderr => result.stderr
|
42
|
-
end
|
43
|
-
if options[:stdout]
|
44
|
-
@env[:ui].info "Command output:\n\n#{result.stdout}\n"
|
45
|
-
end
|
65
|
+
@options = trigger[:options]
|
66
|
+
if @options[:execute]
|
67
|
+
execute(@options[:execute])
|
46
68
|
else
|
47
69
|
@logger.debug("Trigger command not found.")
|
48
70
|
end
|
@@ -7,12 +7,12 @@ module VagrantPlugins
|
|
7
7
|
@triggers = []
|
8
8
|
end
|
9
9
|
|
10
|
-
def after(
|
11
|
-
|
10
|
+
def after(actions, options = {})
|
11
|
+
add_trigger(actions, :after, options)
|
12
12
|
end
|
13
13
|
|
14
|
-
def before(
|
15
|
-
|
14
|
+
def before(actions, options = {})
|
15
|
+
add_trigger(actions, :before, options)
|
16
16
|
end
|
17
17
|
|
18
18
|
def validate(machine)
|
@@ -24,6 +24,14 @@ module VagrantPlugins
|
|
24
24
|
|
25
25
|
{ "triggers" => errors }
|
26
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def add_trigger(actions, condition, options)
|
31
|
+
Array(actions).each do |action|
|
32
|
+
@triggers << { :action => action, :condition => :before, :options => options }
|
33
|
+
end
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -23,8 +23,10 @@ module VagrantPlugins
|
|
23
23
|
|
24
24
|
action_hook(:trigger, Plugin::ALL_ACTIONS) do |hook|
|
25
25
|
require_relative "action"
|
26
|
-
|
27
|
-
|
26
|
+
unless ENV["VAGRANT_NO_TRIGGERS"]
|
27
|
+
hook.prepend(Action.action_trigger(:before))
|
28
|
+
hook.append(Action.action_trigger(:after))
|
29
|
+
end
|
28
30
|
end
|
29
31
|
|
30
32
|
config(:trigger) do
|
data/locales/en.yml
CHANGED
@@ -5,4 +5,8 @@ en:
|
|
5
5
|
The command "%{command}" returned a failed exit code. The
|
6
6
|
error output is shown below:
|
7
7
|
|
8
|
-
%{stderr}
|
8
|
+
%{stderr}
|
9
|
+
command_unavailable: |-
|
10
|
+
The executable "%{command}" Vagrant is trying to trigger
|
11
|
+
was not found. If you know it is not in the PATH, please
|
12
|
+
specify its absolute path using the :append_to_path option.
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "vagrant/util/platform"
|
2
3
|
require "vagrant/util/subprocess"
|
3
4
|
|
4
5
|
describe VagrantPlugins::Triggers::Action::Trigger do
|
@@ -14,65 +15,104 @@ describe VagrantPlugins::Triggers::Action::Trigger do
|
|
14
15
|
|
15
16
|
let(:result) { double("result", :exit_code => 0, :stderr => stderr) }
|
16
17
|
let(:stderr) { double("stderr") }
|
17
|
-
|
18
|
+
|
18
19
|
before do
|
19
20
|
@triggers = [ { :action => machine_action, :condition => condition, :options => { :execute => "foo" } } ]
|
20
21
|
machine.stub_chain(:config, :trigger, :triggers).and_return(@triggers)
|
21
|
-
Vagrant::Util::Subprocess.stub(:execute => result)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
context "with a regular command" do
|
25
|
+
before do
|
26
|
+
Vagrant::Util::Subprocess.stub(:execute => result)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should skip :environment_load and :environment_unload actions" do
|
30
|
+
[:environment_load, :environment_unload].each do |action|
|
31
|
+
env.stub(:[]).with(:action_name).and_return(action)
|
32
|
+
env.should_not_receive(:[]).with(:machine_action)
|
33
|
+
described_class.new(app, env, condition).call(env)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "shouldn't fire if machine action is not defined" do
|
38
|
+
env.stub(:[]).with(:action_name)
|
39
|
+
env.stub(:[]).with(:machine_action).and_return(nil)
|
40
|
+
env.should_not_receive(:[]).with(:machine)
|
28
41
|
described_class.new(app, env, condition).call(env)
|
29
42
|
end
|
30
|
-
end
|
31
43
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
described_class.new(app, env, condition).call(env)
|
37
|
-
end
|
44
|
+
it "should fire trigger when all conditions are satisfied" do
|
45
|
+
Vagrant::Util::Subprocess.should_receive(:execute).with("foo")
|
46
|
+
described_class.new(app, env, condition).call(env)
|
47
|
+
end
|
38
48
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
49
|
+
it "should fire all defined triggers" do
|
50
|
+
@triggers << { :action => machine_action, :condition => condition, :options => { :execute => "bar" } }
|
51
|
+
Vagrant::Util::Subprocess.should_receive(:execute).twice
|
52
|
+
described_class.new(app, env, condition).call(env)
|
53
|
+
end
|
43
54
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
it "shouldn't execute trigger with no command" do
|
56
|
+
@triggers[0][:options] = {}
|
57
|
+
Vagrant::Util::Subprocess.should_not_receive(:execute)
|
58
|
+
described_class.new(app, env, condition).call(env)
|
59
|
+
end
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
61
|
+
it "shouldn't fire trigger when condition doesn't match" do
|
62
|
+
@triggers[0][:condition] = "blah"
|
63
|
+
Vagrant::Util::Subprocess.should_not_receive(:execute)
|
64
|
+
described_class.new(app, env, condition).call(env)
|
65
|
+
end
|
55
66
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
67
|
+
it "shouldn't fire trigger when action doesn't match" do
|
68
|
+
@triggers[0][:action] = "blah"
|
69
|
+
Vagrant::Util::Subprocess.should_not_receive(:execute)
|
70
|
+
described_class.new(app, env, condition).call(env)
|
71
|
+
end
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
73
|
+
it "should raise an error if executed command exits with non-zero code" do
|
74
|
+
result.stub(:exit_code => 1)
|
75
|
+
expect { described_class.new(app, env, condition).call(env) }.to raise_error(VagrantPlugins::Triggers::Errors::CommandFailed)
|
76
|
+
end
|
67
77
|
|
68
|
-
|
69
|
-
|
70
|
-
|
78
|
+
it "shouldn't raise an error if executed command exits with non-zero code but :force option was specified" do
|
79
|
+
@triggers[0][:options][:force] = true
|
80
|
+
result.stub(:exit_code => 1)
|
81
|
+
expect { described_class.new(app, env, condition).call(env) }.not_to raise_error()
|
82
|
+
end
|
71
83
|
end
|
72
84
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
context "with a command not in the PATH" do
|
86
|
+
before do
|
87
|
+
@tmp_dir = Vagrant::Util::Platform.windows? ? ENV["USERPROFILE"] : ENV["HOME"]
|
88
|
+
File.open("#{@tmp_dir}/foo", "w+", 0700) { |file| }
|
89
|
+
File.stub(:executable? => false)
|
90
|
+
File.stub(:executable?).with("#{@tmp_dir}/foo").and_return(true)
|
91
|
+
end
|
92
|
+
|
93
|
+
after do
|
94
|
+
File.delete("#{@tmp_dir}/foo")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should raise a CommandUnavailable error by default" do
|
98
|
+
expect { described_class.new(app, env, condition).call(env) }.to raise_error(VagrantPlugins::Triggers::Errors::CommandUnavailable)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should raise a CommandUnavailable error on Windows" do
|
102
|
+
Vagrant::Util::Platform.stub(:windows? => true)
|
103
|
+
expect { described_class.new(app, env, condition).call(env) }.to raise_error(VagrantPlugins::Triggers::Errors::CommandUnavailable)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should honor the :append_to_path option and restore original path after execution" do
|
107
|
+
@triggers[0][:options][:append_to_path] = @tmp_dir
|
108
|
+
original_path = ENV["PATH"]
|
109
|
+
described_class.new(app, env, condition).call(env)
|
110
|
+
expect(ENV["PATH"]).to eq(original_path)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should accept an array for the :append_to_path option" do
|
114
|
+
@triggers[0][:options][:append_to_path] = [@tmp_dir, @tmp_dir]
|
115
|
+
expect { described_class.new(app, env, condition).call(env) }.not_to raise_error()
|
116
|
+
end
|
77
117
|
end
|
78
118
|
end
|
@@ -20,6 +20,11 @@ describe VagrantPlugins::Triggers::Config do
|
|
20
20
|
config.after :up, :exec => "echo ls"
|
21
21
|
expect(config.triggers).to have(2).items
|
22
22
|
end
|
23
|
+
|
24
|
+
it "should record multiple entries if the action is an array" do
|
25
|
+
config.before [:up, :halt], :exec => "echo ls"
|
26
|
+
expect(config.triggers).to have(2).items
|
27
|
+
end
|
23
28
|
end
|
24
29
|
|
25
30
|
describe "validation" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vagrant-triggers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -66,6 +66,7 @@ executables: []
|
|
66
66
|
extensions: []
|
67
67
|
extra_rdoc_files: []
|
68
68
|
files:
|
69
|
+
- CHANGELOG.md
|
69
70
|
- Gemfile
|
70
71
|
- lib/vagrant-triggers/action/trigger.rb
|
71
72
|
- lib/vagrant-triggers/action.rb
|
@@ -97,7 +98,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
98
|
version: '0'
|
98
99
|
segments:
|
99
100
|
- 0
|
100
|
-
hash: -
|
101
|
+
hash: -1012364614977122118
|
101
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
103
|
none: false
|
103
104
|
requirements:
|
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
107
|
version: '0'
|
107
108
|
segments:
|
108
109
|
- 0
|
109
|
-
hash: -
|
110
|
+
hash: -1012364614977122118
|
110
111
|
requirements: []
|
111
112
|
rubyforge_project:
|
112
113
|
rubygems_version: 1.8.23
|