vagrant-triggers 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- config.trigger.before :command, :execute => "script"
21
+ trigger.before :command, { :option => "value", ... }
22
22
  ```
23
23
 
24
24
  ```
25
- config.trigger.after :command, :execute => "script"
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
- :halt => "controlvm 22aed8b3-d246-40d5-8ad4-176c17552c43 acpipowerbutton",
44
- :resume => "startvm 22aed8b3-d246-40d5-8ad4-176c17552c43 --type headless",
45
- :suspend => "controlvm 22aed8b3-d246-40d5-8ad4-176c17552c43 savestate",
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
- raw_command = options[:execute]
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(action, options = {})
11
- @triggers << { :action => action, :condition => :after, :options => options }
10
+ def after(actions, options = {})
11
+ add_trigger(actions, :after, options)
12
12
  end
13
13
 
14
- def before(action, options = {})
15
- @triggers << { :action => action, :condition => :before, :options => options }
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
@@ -8,6 +8,10 @@ module VagrantPlugins
8
8
  class CommandFailed < VagrantTriggerError
9
9
  error_key(:command_failed)
10
10
  end
11
+
12
+ class CommandUnavailable < VagrantTriggerError
13
+ error_key(:command_unavailable)
14
+ end
11
15
  end
12
16
  end
13
17
  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
- hook.prepend(Action.action_trigger(:before))
27
- hook.append(Action.action_trigger(:after))
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
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Triggers
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -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
- it "should skip :environment_load and :environment_unload actions" do
25
- [:environment_load, :environment_unload].each do |action|
26
- env.stub(:[]).with(:action_name).and_return(action)
27
- env.should_not_receive(:[]).with(:machine_action)
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
- it "shouldn't fire if machine action is not defined" do
33
- env.stub(:[]).with(:action_name)
34
- env.stub(:[]).with(:machine_action).and_return(nil)
35
- env.should_not_receive(:[]).with(:machine)
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
- it "should fire trigger when all conditions are satisfied" do
40
- Vagrant::Util::Subprocess.should_receive(:execute).with("foo")
41
- described_class.new(app, env, condition).call(env)
42
- end
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
- it "should fire all defined triggers" do
45
- @triggers << { :action => machine_action, :condition => condition, :options => { :execute => "bar" } }
46
- Vagrant::Util::Subprocess.should_receive(:execute).twice
47
- described_class.new(app, env, condition).call(env)
48
- end
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
- it "shouldn't execute trigger with no command" do
51
- @triggers[0][:options] = {}
52
- Vagrant::Util::Subprocess.should_not_receive(:execute)
53
- described_class.new(app, env, condition).call(env)
54
- end
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
- it "shouldn't fire trigger when condition doesn't match" do
57
- @triggers[0][:condition] = "blah"
58
- Vagrant::Util::Subprocess.should_not_receive(:execute)
59
- described_class.new(app, env, condition).call(env)
60
- end
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
- it "shouldn't fire trigger when action doesn't match" do
63
- @triggers[0][:action] = "blah"
64
- Vagrant::Util::Subprocess.should_not_receive(:execute)
65
- described_class.new(app, env, condition).call(env)
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
- it "should raise an error if executed command exits with non-zero code" do
69
- result.stub(:exit_code => 1)
70
- expect { described_class.new(app, env, condition).call(env) }.to raise_error(VagrantPlugins::Triggers::Errors::CommandFailed)
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
- it "shouldn't raise an error if executed command exits with non-zero code but :force option was specified" do
74
- @triggers[0][:options][:force] = true
75
- result.stub(:exit_code => 1)
76
- expect { described_class.new(app, env, condition).call(env) }.to_not raise_error()
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.1.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-08-19 00:00:00.000000000 Z
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: -3600971872626738108
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: -3600971872626738108
110
+ hash: -1012364614977122118
110
111
  requirements: []
111
112
  rubyforge_project:
112
113
  rubygems_version: 1.8.23