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.
@@ -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