vagrant-devcommands 0.7.2 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2286eb231a9e6537418f0a60167b17b3022f386c
4
- data.tar.gz: 9faa6c649189f8626c622441c4ff9bb26a37a67c
3
+ metadata.gz: 32211b187c89aedb28b7e3b084553e07030fa971
4
+ data.tar.gz: 2be8cec37c79264056981c70acdd490ebdeb4d18
5
5
  SHA512:
6
- metadata.gz: f993ab2fff187f77800617b595e443ff15e3a7b4376bd8a733a0e7c2e30589475fa6790f29ee2d2cc6df910f015a7a7e52758f234966d100a93988096c0ccff6
7
- data.tar.gz: 3c2ae42e63a90567cb49085f5646143f115db29ec960def9eacce7b0b1704c0c3b717bc5d3c219544c07cfe274fb6923129448ea8fff653b1a76cbf61cfa2e8f
6
+ metadata.gz: a6a418391c08c271c88d3c334a88c1ad02173725fe2300bc81523ce9302b3344068769cdf52c23da6fd869a307bf8ba01f0a78855aca86afbaa2a42de384e858
7
+ data.tar.gz: cabdfa5024114480df1ae410ddfd7d78928a62099672fd6909838260fa0e314f708a73abd00d08e01d57917b14fe203a1149217aeefec1735cb1aca91466af37
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.8.0 (2017-04-19)
4
+
5
+ - Enhancements
6
+ - Chains can be defined to execute multiple commands in order
7
+ - Commands can define flags (always optional!) for script interpolation
8
+
3
9
  ## v0.7.2 (2017-03-12)
4
10
 
5
11
  - Bug fixes
data/README.md CHANGED
@@ -22,6 +22,8 @@ vagrant run your_command
22
22
  vagrant run your_box your_command
23
23
  ```
24
24
 
25
+ This runs commands or alternatively a command chain with the passed name.
26
+
25
27
  ### Command Definition
26
28
 
27
29
  Add to a `Commandfile` besides your `Vagrantfile`:
@@ -30,11 +32,11 @@ Add to a `Commandfile` besides your `Vagrantfile`:
30
32
  command 'basic', 'hostname'
31
33
 
32
34
  command 'with_options',
33
- box: :my_box,
34
- desc: 'executes "hostname" on the box "my_box"',
35
- script: 'hostname',
36
- usage: 'vagrant run %{command}',
37
- help: <<-eoh
35
+ box: :my_box,
36
+ desc: 'executes "hostname" on the box "my_box"',
37
+ script: 'hostname',
38
+ usage: 'vagrant run %{command}',
39
+ help: <<-eoh
38
40
  I am the help message for the command "with_options".
39
41
  I get displayed when running "vagrant run help with_options".
40
42
 
@@ -97,6 +99,36 @@ Escaping rules are defined as `{ "char_to_escape": "char_to_use_as_escape" }`.
97
99
  These are applied prior to interpolation into the command. Regular ruby escaping
98
100
  rules apply.
99
101
 
102
+ #### Commands with Flags
103
+
104
+ Every command can be associated with (by definition optional) flags available
105
+ for later command interpolation:
106
+
107
+ ```ruby
108
+ command 'with_flags',
109
+ flags: {
110
+ f_standard: { desc: "standard flag" },
111
+ f_valued: { value: "--f_modified" }
112
+ },
113
+ script: 'echo "flags: %{f_standard}"'
114
+ ```
115
+
116
+ This definition allows the following executions:
117
+
118
+ ```shell
119
+ # will execute 'echo "flags: "'
120
+ vagrant run with_flags
121
+
122
+ # will execute 'echo "flags: --f_standard"'
123
+ vagrant run with_flags --f_standard
124
+
125
+ # will execute 'echo "flags: --f_modified"'
126
+ vagrant run with_flags --f_valued
127
+ ```
128
+
129
+ By default a flag gets interpolated as "--#{flagname}". If a value is defined
130
+ this value will be interpolated unmodified.
131
+
100
132
  #### Commands defined by Lambda/Proc
101
133
 
102
134
  You can (more or less) dynamically generate your scripts by defining the
@@ -111,8 +143,7 @@ These will be evaluated when running the command.
111
143
 
112
144
  Every rule from regular scripts (parameters, escaping "%", ...) still apply.
113
145
 
114
-
115
- ### Experimental: global command definitions
146
+ ### Global Command Definitions
116
147
 
117
148
  To have commands available even wihout a `Commandfile` you can define the
118
149
  globally. To do this just create a file named `.vagrant.devcommands` in your
@@ -126,7 +157,70 @@ ruby -e "require 'pathname'; puts Pathname.new(Dir.home).join('.vagrant.devcomma
126
157
 
127
158
  Any commands defined there will silently be overwritten by a local definition.
128
159
 
129
- ### Abort parsing inside Commandfile
160
+ ### Experimental: Chain Definitions
161
+
162
+ You can define command chains to execute multiple commands in order:
163
+
164
+ ```ruby
165
+ chain 'my_chain',
166
+ break_on_error: false,
167
+ commands: [
168
+ { command: 'first' },
169
+ { command: 'second' },
170
+ { command: 'third' }
171
+ ],
172
+ desc: 'Command chain for three commands.',
173
+ help: <<-eoh
174
+ I am the help message for the chain "my_chain".
175
+ I get displayed when running "vagrant run help my_chain".
176
+
177
+ The usage printed above the help can interpolate the name
178
+ of the command name using %{command}.
179
+ eoh
180
+ ```
181
+
182
+ The configured commands will be executed in the order defined.
183
+
184
+ If one or more of your commands requires parameters all of them have to be
185
+ passed to the chain execution.
186
+
187
+ By default a chain breaks upon the first non-zero return value of any
188
+ configured command. To deactivate this behaviour you can set `:break_on_error`
189
+ to `false`. Any value other than `false` will stick to the default.
190
+
191
+ #### Chain Definitions with Pre-Defined Parameters
192
+
193
+ If required you can modify the arguments given to each chain element by setting
194
+ additional/custom argv values for a single chain element:
195
+
196
+ ```ruby
197
+ command 'chainecho',
198
+ parameters: { first: {}, second: {} },
199
+ script: 'echo %{first} %{second}'
200
+
201
+ chain 'my_customized_chain',
202
+ commands: [
203
+ { command: 'chainecho', argv: ['--first="param"'] },
204
+ { command: 'chainecho' },
205
+ { command: 'chainecho', argv: ['--first="param"', '--second="param"'] }
206
+ ]
207
+ ```
208
+
209
+ Running the chain will execute the following commands:
210
+
211
+ ```shell
212
+ > vagrant run my_customized_chain --first="initial" --second="initial"
213
+
214
+ vagrant run chainecho --first="param" --second="initial"
215
+ vagrant run chainecho --first="initial" --second="initial"
216
+ vagrant run chainecho --first="param" --second="param"
217
+ ```
218
+
219
+ By default every command will be executed using the box defined by the command
220
+ itself or the only one available. You can, however, run the complete chain
221
+ against a specific box using `vagrant run your_box your_chain`.
222
+
223
+ ### Abort Parsing inside Commandfile
130
224
 
131
225
  If you, for whatever reasons, want to abort further parsing of a `Commandfile`
132
226
  you can simple return from it:
@@ -166,10 +260,10 @@ a sigil notation like the following:
166
260
 
167
261
  ```ruby
168
262
  command 'long_running_task',
169
- script: %(cd /path/to/somewhere \
170
- && echo "starting long running task" \
171
- && ./long_running_task.sh \
172
- && echo "finished long running task")
263
+ script: %(cd /path/to/somewhere \
264
+ && echo "starting long running task" \
265
+ && ./long_running_task.sh \
266
+ && echo "finished long running task")
173
267
  ```
174
268
 
175
269
  Using a quote delimited command definition might otherwise result in not that
@@ -2,10 +2,13 @@ module VagrantPlugins
2
2
  module DevCommands
3
3
  # Defines the executable vagrant command
4
4
  class Command < Vagrant.plugin(2, :command)
5
- UTIL = VagrantPlugins::DevCommands::Util
5
+ NAMESPACE_RUNNER = VagrantPlugins::DevCommands::Runner
6
+ MESSAGES = VagrantPlugins::DevCommands::Messages
6
7
 
7
8
  def self.synopsis
8
- 'runs vagrant commands from a Commandfile'
9
+ synopsis = VagrantPlugins::DevCommands::SYNOPSIS
10
+
11
+ synopsis[0, 1].downcase + synopsis[1..-1]
9
12
  end
10
13
 
11
14
  def initialize(argv, env)
@@ -17,55 +20,52 @@ module VagrantPlugins
17
20
  def execute
18
21
  return 127 unless read_commandfile
19
22
 
20
- command = argv_command
23
+ command = Util.argv_command(@argv, @env)
21
24
 
22
- return 127 unless check_command(command)
25
+ return 127 unless non_empty?(command)
26
+ return 127 unless available?(command)
23
27
 
24
28
  return run_internal(command) if @registry.reserved_command?(command)
25
29
 
26
- run @registry.commands[command]
30
+ run command
27
31
  end
28
32
 
29
- private
30
-
31
- attr_accessor :registry
33
+ def proxy_with_target_vms(names = nil, options = nil, &block)
34
+ # allows public access to protected method with_target_vms
35
+ with_target_vms(names, options, &block)
36
+ end
32
37
 
33
- def check_command(command)
34
- unless command
35
- run_internal('help')
36
- return false
37
- end
38
+ private
38
39
 
39
- unless @registry.valid_command?(command)
40
+ def available?(command)
41
+ unless @registry.available?(command)
40
42
  display_error("Invalid command \"#{command}\"!")
41
43
  run_internal('help', ['--commands'])
42
-
43
- return false
44
44
  end
45
45
 
46
- true
46
+ @registry.available?(command)
47
47
  end
48
48
 
49
- def argv_command
50
- return nil if @argv.empty?
51
-
52
- command = @argv[0].to_s
53
- command = @argv[1].to_s if UTIL.machine_name?(@argv[1].to_s,
54
- @env.machine_index)
55
-
56
- command
49
+ def display_error(msg, post_ln = false)
50
+ @env.ui.error msg
51
+ @env.ui.error '' if post_ln
57
52
  end
58
53
 
59
- def display_error(msg)
60
- @env.ui.error msg
61
- @env.ui.error ''
54
+ def non_empty?(command)
55
+ unless command
56
+ run_internal('help')
57
+ return false
58
+ end
59
+
60
+ true
62
61
  end
63
62
 
64
63
  def read_commandfile
65
- commandfile = CommandFile.new(@env)
64
+ commandfile = Commandfile.new(@env)
66
65
 
67
66
  unless commandfile.exist?
68
- @env.ui.error 'Missing Commandfile'
67
+ MESSAGES.missing_commandfile(&@env.ui.method(:error))
68
+ MESSAGES.pre_ln(:plugin_readme, &@env.ui.method(:info))
69
69
 
70
70
  return false
71
71
  end
@@ -76,56 +76,39 @@ module VagrantPlugins
76
76
  end
77
77
 
78
78
  def run(command)
79
- argv = run_argv
80
- box = run_box(command)
81
- script = run_script(command, argv)
82
-
83
- return 2 unless script
84
-
85
- with_target_vms(box, single_target: true) do |vm|
86
- env = vm.action(:ssh_run,
87
- ssh_opts: { extra_args: ['-q'] },
88
- ssh_run_command: script)
89
-
90
- return env[:ssh_run_exit_status] || 0
91
- end
92
- end
93
-
94
- def run_argv
95
- argv = @argv.dup
96
-
97
- argv.shift if UTIL.machine_name?(argv[0].to_s, @env.machine_index)
98
- argv.shift
99
- argv
100
- end
79
+ runner = runner_for(command)
80
+ runnable = runnable_for(command)
101
81
 
102
- def run_box(cmd)
103
- box = nil
104
- box = cmd.box.to_s if cmd.box
105
- box = @argv[0] if UTIL.machine_name?(@argv[0].to_s, @env.machine_index)
82
+ runner.run(runnable)
83
+ rescue RuntimeError => e
84
+ display_error(e.message, true)
85
+ run_internal('help', [command])
106
86
 
107
- box
87
+ nil
108
88
  end
109
89
 
110
90
  def run_internal(command, args = nil)
111
- Internal.new(@env, @registry).run(command, args || run_argv)
112
- end
91
+ runner = NAMESPACE_RUNNER::InternalCommand.new(
92
+ self, @argv, @env, @registry
93
+ )
113
94
 
114
- def run_script(command, argv)
115
- command.run_script(argv)
116
- rescue KeyError => e
117
- param = e.message.match(/{(.+)}/).captures.first
118
-
119
- run_script_error(command.name, "missing parameter '#{param}'")
120
- rescue OptionParser::InvalidOption => e
121
- run_script_error(command.name, "invalid parameter '#{e.args.first}'")
95
+ runner.run(command, args)
122
96
  end
123
97
 
124
- def run_script_error(command, error)
125
- display_error("Could not execute #{command}: #{error}!")
126
- run_internal('help', [command])
98
+ def runnable_for(command)
99
+ if @registry.valid_command?(command)
100
+ @registry.commands[command]
101
+ else
102
+ @registry.chains[command]
103
+ end
104
+ end
127
105
 
128
- nil
106
+ def runner_for(command)
107
+ if @registry.valid_command?(command)
108
+ NAMESPACE_RUNNER::Command.new(self, @argv, @env, @registry)
109
+ else
110
+ NAMESPACE_RUNNER::Chain.new(self, @argv, @env, @registry)
111
+ end
129
112
  end
130
113
  end
131
114
  end
@@ -3,7 +3,7 @@ require 'pathname'
3
3
  module VagrantPlugins
4
4
  module DevCommands
5
5
  # Loads and handles the Commandfile
6
- class CommandFile
6
+ class Commandfile
7
7
  def initialize(env)
8
8
  @env = env
9
9
  end
@@ -26,12 +26,10 @@ module VagrantPlugins
26
26
 
27
27
  private
28
28
 
29
- attr_accessor :env
30
-
31
29
  def find_commandfile
32
30
  return nil unless @env.root_path
33
31
 
34
- %w(Commandfile commandfile).each do |commandfile|
32
+ %w[Commandfile commandfile].each do |commandfile|
35
33
  current_path = @env.root_path.join(commandfile)
36
34
 
37
35
  return current_path if current_path.file?
@@ -0,0 +1,62 @@
1
+ module VagrantPlugins
2
+ module DevCommands
3
+ module HelpPrinter
4
+ # Prints help for a command chain
5
+ class Chain
6
+ UTIL = VagrantPlugins::DevCommands::Util
7
+ MESSAGES = VagrantPlugins::DevCommands::Messages
8
+
9
+ def initialize(env)
10
+ @env = env
11
+ end
12
+
13
+ def output(chain)
14
+ header(chain)
15
+ commands(chain)
16
+ body(chain.help)
17
+ end
18
+
19
+ private
20
+
21
+ def body(help)
22
+ return message(:chain_no_help, true) if help.nil?
23
+
24
+ info(help.strip, true)
25
+ end
26
+
27
+ def chain_help_line(cmd)
28
+ return cmd[:command] unless cmd.key?(:argv)
29
+
30
+ "#{cmd[:command]} #{cmd[:argv]}"
31
+ end
32
+
33
+ def commands(chain)
34
+ info('Chained commands (in order):', true)
35
+
36
+ chain.commands.each do |cmd, _options|
37
+ info(UTIL.padded_columns(0, chain_help_line(cmd)))
38
+ end
39
+ end
40
+
41
+ def header(chain)
42
+ usage = "vagrant run [box] #{chain.name}"
43
+
44
+ info(I18n.t('vagrant_devcommands.internal.help.usage', what: usage))
45
+ end
46
+
47
+ def info(msg, pre_ln = false)
48
+ @env.ui.info '' if pre_ln
49
+ @env.ui.info msg
50
+ end
51
+
52
+ def message(msg, pre_ln = false)
53
+ if pre_ln
54
+ MESSAGES.pre_ln(msg, &@env.ui.method(:info))
55
+ else
56
+ MESSAGES.public_send(msg, &@env.ui.method(:info))
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ module VagrantPlugins
2
+ module DevCommands
3
+ module HelpPrinter
4
+ # Prints help for a command
5
+ class Command
6
+ UTIL = VagrantPlugins::DevCommands::Util
7
+ MESSAGES = VagrantPlugins::DevCommands::Messages
8
+
9
+ def initialize(env)
10
+ @env = env
11
+ end
12
+
13
+ def output(command)
14
+ header(command)
15
+ arguments(command.parameters, 'Parameters')
16
+ arguments(command.flags, 'Flags')
17
+ body(command.help)
18
+ end
19
+
20
+ private
21
+
22
+ def arguments(arguments, title)
23
+ return if arguments.nil?
24
+
25
+ info("#{title}:", true)
26
+ arguments_body(arguments)
27
+ end
28
+
29
+ def arguments_body(arguments)
30
+ pad_to = UTIL.pad_to(arguments)
31
+
32
+ arguments.sort.each do |name, options|
33
+ info(UTIL.padded_columns(pad_to, name, options[:desc]))
34
+ end
35
+ end
36
+
37
+ def body(help)
38
+ return message(:command_no_help, true) if help.nil?
39
+
40
+ info(help.strip, true)
41
+ end
42
+
43
+ def header(command)
44
+ usage = "vagrant run [box] #{command.name}"
45
+ usage = usage_params(usage, command)
46
+
47
+ unless command.usage.nil?
48
+ usage = format(command.usage, command: command)
49
+ end
50
+
51
+ info(I18n.t('vagrant_devcommands.internal.help.usage', what: usage))
52
+ end
53
+
54
+ def info(msg, pre_ln = false)
55
+ @env.ui.info '' if pre_ln
56
+ @env.ui.info msg
57
+ end
58
+
59
+ def message(msg, pre_ln = false)
60
+ if pre_ln
61
+ MESSAGES.pre_ln(msg, &@env.ui.method(:info))
62
+ else
63
+ MESSAGES.public_send(msg, &@env.ui.method(:info))
64
+ end
65
+ end
66
+
67
+ def usage_params(usage, command)
68
+ [
69
+ usage,
70
+ UTIL.collect_mandatory_params(command.parameters || {}),
71
+ UTIL.collect_optional_params(command.parameters || {}),
72
+ UTIL.collect_flags(command.flags || {})
73
+ ].flatten.compact.join(' ').strip
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end