vagrant-devcommands 0.8.0 → 0.9.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: 32211b187c89aedb28b7e3b084553e07030fa971
4
- data.tar.gz: 2be8cec37c79264056981c70acdd490ebdeb4d18
3
+ metadata.gz: 1cdd09f90d6b080675f4b15e4fb9ec4ce3101cf2
4
+ data.tar.gz: 9968d30256b15ce9247ad33c39e1e1c39a3b6daf
5
5
  SHA512:
6
- metadata.gz: a6a418391c08c271c88d3c334a88c1ad02173725fe2300bc81523ce9302b3344068769cdf52c23da6fd869a307bf8ba01f0a78855aca86afbaa2a42de384e858
7
- data.tar.gz: cabdfa5024114480df1ae410ddfd7d78928a62099672fd6909838260fa0e314f708a73abd00d08e01d57917b14fe203a1149217aeefec1735cb1aca91466af37
6
+ metadata.gz: aa62d03f315aab6378feb9f9ffff8d7c3f3f22930c2b7ab177ab7afea46e34761a7cab7a5c08c4ce249cff2d6e51e288fb6a1d274663ff21b529c5d242bed272
7
+ data.tar.gz: 6581d21c82f57b249c645844f4e64a5cebe36c4e5171a2992e1a9a9ba914fb4ae710b3d5ae7bfcbe56deb021e5f273910071acfdf655eedf3219d99b85419533
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.9.0 (2017-05-20)
4
+
5
+ - Enhancements
6
+ - Every command in a chain can specify the machine to be used
7
+ - If an unknown command is requested a possible alternative
8
+ is suggested based on the calculated
9
+ [Jaro-Winkler distance](https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance)
10
+
11
+ - Deprecations
12
+ - The configuration parameter `:box` has been renamed to `:machine` to
13
+ match the vagrant naming. Support for the old configuration will be
14
+ dropped in a future version
15
+
3
16
  ## v0.8.0 (2017-04-19)
4
17
 
5
18
  - Enhancements
data/README.md CHANGED
@@ -15,11 +15,11 @@ vagrant run
15
15
 
16
16
  ```shell
17
17
  # single-vm environment
18
- # or multi-vm environment with :box option
18
+ # or multi-vm environment with :machine option
19
19
  vagrant run your_command
20
20
 
21
21
  # multi-vm environment
22
- vagrant run your_box your_command
22
+ vagrant run your_machine your_command
23
23
  ```
24
24
 
25
25
  This runs commands or alternatively a command chain with the passed name.
@@ -32,8 +32,8 @@ Add to a `Commandfile` besides your `Vagrantfile`:
32
32
  command 'basic', 'hostname'
33
33
 
34
34
  command 'with_options',
35
- box: :my_box,
36
- desc: 'executes "hostname" on the box "my_box"',
35
+ machine: :my_machine,
36
+ desc: 'executes "hostname" on the machine "my_machine"',
37
37
  script: 'hostname',
38
38
  usage: 'vagrant run %{command}',
39
39
  help: <<-eoh
@@ -216,9 +216,37 @@ vagrant run chainecho --first="initial" --second="initial"
216
216
  vagrant run chainecho --first="param" --second="param"
217
217
  ```
218
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`.
219
+ By default every command will be executed using the machine defined by the
220
+ command itself or the only one available. You can, however, run the complete
221
+ chain against a specific machine using `vagrant run your_machine your_chain`.
222
+
223
+ #### Chain Definitions with Specific Machines
224
+
225
+ If required you can modify the machine a box is run on:
226
+
227
+ ```ruby
228
+ command 'chainhost',
229
+ script: 'hostname'
230
+
231
+ chain 'customized_chain_machine',
232
+ commands: [
233
+ { command: 'chainhost' },
234
+ { command: 'chainecho', machine: 'secondary' },
235
+ { command: 'chainecho', machine: 'tertiary' }
236
+ ]
237
+
238
+ Running the chain will execute the following commands:
239
+
240
+ ```shell
241
+ > vagrant run customized_chain_machine
242
+
243
+ vagrant run chainhost
244
+ vagrant run secondary chainhost
245
+ vagrant run tertiary chainhost
246
+ ```
247
+
248
+ This configuration can itself be modified by passing a machine name to run
249
+ all chain commands on using using `vagrant run your_machine your_chain`.
222
250
 
223
251
  ### Abort Parsing inside Commandfile
224
252
 
@@ -250,7 +278,7 @@ evaluating a local one.
250
278
  ### SSH
251
279
 
252
280
  If you are using this plugin on a Windows host system, please make sure your
253
- regular `vagrant ssh [box]` succeeds. In some cases you may need to add the
281
+ regular `vagrant ssh [machine]` succeeds. In some cases you may need to add the
254
282
  `ssh.exe` (i.e. from a git installation) manually to your `%PATH%`.
255
283
 
256
284
  ### Command Definition
@@ -8,6 +8,7 @@ require 'vagrant/devcommands/version'
8
8
 
9
9
  require 'vagrant/devcommands/internal_spec'
10
10
  require 'vagrant/devcommands/messages'
11
+ require 'vagrant/devcommands/util/jaro_winkler'
11
12
  require 'vagrant/devcommands/util'
12
13
 
13
14
  require 'vagrant/devcommands/model/chain'
@@ -4,6 +4,7 @@ module VagrantPlugins
4
4
  class Command < Vagrant.plugin(2, :command)
5
5
  NAMESPACE_RUNNER = VagrantPlugins::DevCommands::Runner
6
6
  MESSAGES = VagrantPlugins::DevCommands::Messages
7
+ UTIL = VagrantPlugins::DevCommands::Util
7
8
 
8
9
  def self.synopsis
9
10
  synopsis = VagrantPlugins::DevCommands::SYNOPSIS
@@ -20,6 +21,8 @@ module VagrantPlugins
20
21
  def execute
21
22
  return 127 unless read_commandfile
22
23
 
24
+ deprecated_box_config?
25
+
23
26
  command = Util.argv_command(@argv, @env)
24
27
 
25
28
  return 127 unless non_empty?(command)
@@ -40,13 +43,27 @@ module VagrantPlugins
40
43
  def available?(command)
41
44
  unless @registry.available?(command)
42
45
  display_error("Invalid command \"#{command}\"!")
46
+ did_you_mean(command)
43
47
  run_internal('help', ['--commands'])
44
48
  end
45
49
 
46
50
  @registry.available?(command)
47
51
  end
48
52
 
49
- def display_error(msg, post_ln = false)
53
+ def deprecated_box_config?
54
+ return unless @registry.commands.values.any?(&:deprecated_box_config)
55
+
56
+ MESSAGES.deprecated_box_config(&@env.ui.method(:warn))
57
+ end
58
+
59
+ def did_you_mean(command)
60
+ alternative, score = Util.did_you_mean(command, @registry)
61
+
62
+ display_error("Did you mean #{alternative}?", true) if 0.8 < score
63
+ end
64
+
65
+ def display_error(msg, pre_ln = false, post_ln = false)
66
+ @env.ui.error '' if pre_ln
50
67
  @env.ui.error msg
51
68
  @env.ui.error '' if post_ln
52
69
  end
@@ -81,7 +98,7 @@ module VagrantPlugins
81
98
 
82
99
  runner.run(runnable)
83
100
  rescue RuntimeError => e
84
- display_error(e.message, true)
101
+ display_error(e.message, false, true)
85
102
  run_internal('help', [command])
86
103
 
87
104
  nil
@@ -39,7 +39,7 @@ module VagrantPlugins
39
39
  end
40
40
 
41
41
  def header(chain)
42
- usage = "vagrant run [box] #{chain.name}"
42
+ usage = "vagrant run [machine] #{chain.name}"
43
43
 
44
44
  info(I18n.t('vagrant_devcommands.internal.help.usage', what: usage))
45
45
  end
@@ -41,7 +41,7 @@ module VagrantPlugins
41
41
  end
42
42
 
43
43
  def header(command)
44
- usage = "vagrant run [box] #{command.name}"
44
+ usage = "vagrant run [machine] #{command.name}"
45
45
  usage = usage_params(usage, command)
46
46
 
47
47
  unless command.usage.nil?
@@ -7,7 +7,7 @@ module VagrantPlugins
7
7
  @env = env
8
8
  end
9
9
 
10
- def execute(_args)
10
+ def execute(_argv)
11
11
  @env.ui.info "vagrant-devcommands, version #{VERSION}"
12
12
  end
13
13
  end
@@ -10,6 +10,11 @@ module VagrantPlugins
10
10
  out.call I18n.t("#{I18N_KEY}.chain_no_help")
11
11
  end
12
12
 
13
+ def self.deprecated_box_config(&out)
14
+ out.call I18n.t("#{I18N_KEY}.deprecated_box_config")
15
+ out.call ''
16
+ end
17
+
13
18
  def self.command_no_help(&out)
14
19
  out.call I18n.t("#{I18N_KEY}.command_no_help")
15
20
  end
@@ -11,11 +11,13 @@ module VagrantPlugins
11
11
  attr_reader :parameters
12
12
  attr_reader :script
13
13
 
14
- attr_reader :box
14
+ attr_reader :machine
15
15
  attr_reader :desc
16
16
  attr_reader :help
17
17
  attr_reader :usage
18
18
 
19
+ attr_reader :deprecated_box_config
20
+
19
21
  def initialize(spec)
20
22
  @name = spec[:name]
21
23
 
@@ -23,10 +25,12 @@ module VagrantPlugins
23
25
  @parameters = spec[:parameters]
24
26
  @script = spec[:script]
25
27
 
26
- @box = spec[:box]
27
- @desc = spec[:desc]
28
- @help = spec[:help]
29
- @usage = spec[:usage]
28
+ @machine = spec[:machine] || spec[:box]
29
+ @desc = spec[:desc]
30
+ @help = spec[:help]
31
+ @usage = spec[:usage]
32
+
33
+ @deprecated_box_config = spec.key?(:box)
30
34
  end
31
35
 
32
36
  def run_script(argv)
@@ -87,6 +91,7 @@ module VagrantPlugins
87
91
 
88
92
  wrap_option_values(escape_option_values(options))
89
93
  end
94
+ # rubocop:enable Metrics/MethodLength
90
95
 
91
96
  def wrap_option_values(options)
92
97
  (@parameters || {}).each do |key, conf|
@@ -3,6 +3,8 @@ module VagrantPlugins
3
3
  module Runner
4
4
  # Chain runner
5
5
  class Chain
6
+ UTIL = VagrantPlugins::DevCommands::Util
7
+
6
8
  def initialize(plugin, argv, env, registry)
7
9
  @plugin = plugin
8
10
  @argv = argv
@@ -29,10 +31,22 @@ module VagrantPlugins
29
31
  private
30
32
 
31
33
  def argv_for(command_def)
32
- return @argv unless command_def.key?(:argv)
33
- return @argv unless command_def[:argv].is_a?(Array)
34
+ argv = patch_machine(@argv.dup, command_def)
35
+
36
+ return argv unless command_def.key?(:argv)
37
+ return argv unless command_def[:argv].is_a?(Array)
38
+
39
+ argv + command_def[:argv]
40
+ end
41
+
42
+ def patch_machine(argv, command_def)
43
+ return argv unless command_def.key?(:machine)
34
44
 
35
- @argv + command_def[:argv]
45
+ if UTIL.machine_name?(argv[0].to_s, @env.machine_index)
46
+ argv
47
+ else
48
+ [command_def[:machine].to_s] + argv
49
+ end
36
50
  end
37
51
 
38
52
  def runnable_for(command_def)
@@ -13,13 +13,13 @@ module VagrantPlugins
13
13
  end
14
14
 
15
15
  def run(command)
16
- argv = run_argv
17
- box = run_box(command)
18
- script = run_script(command, argv)
16
+ argv = run_argv
17
+ machine = run_machine(command)
18
+ script = run_script(command, argv)
19
19
 
20
20
  return 2 unless script
21
21
 
22
- @plugin.proxy_with_target_vms(box, single_target: true) do |vm|
22
+ @plugin.proxy_with_target_vms(machine, single_target: true) do |vm|
23
23
  env = vm.action(:ssh_run,
24
24
  ssh_opts: { extra_args: ['-q'] },
25
25
  ssh_run_command: script)
@@ -38,13 +38,13 @@ module VagrantPlugins
38
38
  argv
39
39
  end
40
40
 
41
- def run_box(cmd)
42
- box = nil
43
- box = cmd.box.to_s if cmd.box
44
- box = @argv[0] if UTIL.machine_name?(@argv[0].to_s,
45
- @env.machine_index)
41
+ def run_machine(cmd)
42
+ machine = nil
43
+ machine = cmd.machine.to_s if cmd.machine
44
+ machine = @argv[0] if UTIL.machine_name?(@argv[0].to_s,
45
+ @env.machine_index)
46
46
 
47
- box
47
+ machine
48
48
  end
49
49
 
50
50
  def run_script(command, argv)
@@ -2,6 +2,8 @@ module VagrantPlugins
2
2
  module DevCommands
3
3
  # Utility module
4
4
  class Util
5
+ JARO_WINKLER = VagrantPlugins::DevCommands::Util::JaroWinkler
6
+
5
7
  def self.argv_command(argv, env)
6
8
  return nil if argv.empty?
7
9
 
@@ -29,6 +31,18 @@ module VagrantPlugins
29
31
  end
30
32
  end
31
33
 
34
+ def self.did_you_mean(command, registry)
35
+ alternatives = registry.commands.keys + registry.chains.keys
36
+ distances = {}
37
+
38
+ alternatives.each do |alternative|
39
+ calculator = JARO_WINKLER.new(command, alternative)
40
+ distances[alternative] = calculator.distance
41
+ end
42
+
43
+ distances.max_by { |_k, v| v }
44
+ end
45
+
32
46
  def self.machine_name?(name, machine_index)
33
47
  machine_index.any? { |machine| name == machine.name }
34
48
  end
@@ -0,0 +1,126 @@
1
+ module VagrantPlugins
2
+ module DevCommands
3
+ class Util
4
+ # Jaro Winkler string distance
5
+ #
6
+ # Adapted from:
7
+ # https://github.com/bbatsov/rubocop/blob/ec3123fc3454b080e1100e35480c6466d1240fff/lib/rubocop/string_util.rb
8
+ class JaroWinkler
9
+ BOOST_THRESHOLD = 0.7
10
+ MAX_COMMON_PREFIX_LENGTH = 4
11
+ SCALING_FACTOR = 0.1
12
+
13
+ def initialize(left, right)
14
+ @left = left.to_s
15
+ @right = right.to_s
16
+
17
+ if @left.size < @right.size
18
+ @shorter = @left
19
+ @longer = @right
20
+ else
21
+ @shorter = @right
22
+ @longer = @left
23
+ end
24
+ end
25
+
26
+ def distance
27
+ jaro_distance = compute_distance
28
+
29
+ if jaro_distance >= BOOST_THRESHOLD
30
+ jaro_distance += (
31
+ limited_common_prefix_length.to_f *
32
+ SCALING_FACTOR.to_f *
33
+ (1.0 - jaro_distance)
34
+ )
35
+ end
36
+
37
+ jaro_distance
38
+ end
39
+
40
+ private
41
+
42
+ def common_prefix_length
43
+ @shorter.size.times do |index|
44
+ return index unless @shorter[index] == @longer[index]
45
+ end
46
+
47
+ @shorter.size
48
+ end
49
+
50
+ def compute_distance
51
+ common_chars_a, common_chars_b = find_common_characters
52
+ matched_count = common_chars_a.size
53
+
54
+ return 0.0 if matched_count.zero?
55
+
56
+ transposition_count = count_transpositions(common_chars_a,
57
+ common_chars_b)
58
+
59
+ compute_non_zero_distance(matched_count.to_f, transposition_count)
60
+ end
61
+
62
+ def compute_non_zero_distance(matched_count, transposition_count)
63
+ sum = (matched_count / @shorter.size.to_f) +
64
+ (matched_count / @longer.size.to_f) +
65
+ ((matched_count - transposition_count / 2) / matched_count)
66
+
67
+ sum / 3.0
68
+ end
69
+
70
+ def count_transpositions(common_chars_a, common_chars_b)
71
+ common_chars_a.size.times.count do |index|
72
+ common_chars_a[index] != common_chars_b[index]
73
+ end
74
+ end
75
+
76
+ # rubocop:disable Metrics/MethodLength
77
+ def find_common_characters
78
+ common_chars_of_shorter = Array.new(@shorter.size)
79
+ common_chars_of_longer = Array.new(@longer.size)
80
+
81
+ @shorter.each_char.with_index do |shorter_char, shorter_index|
82
+ matching_index_range(shorter_index).each do |longer_index|
83
+ longer_char = @longer.chars[longer_index]
84
+
85
+ next unless shorter_char == longer_char
86
+
87
+ common_chars_of_shorter[shorter_index] = shorter_char
88
+ common_chars_of_longer[longer_index] = longer_char
89
+
90
+ # Mark the matching character as already used
91
+ @longer.chars[longer_index] = nil
92
+
93
+ break
94
+ end
95
+ end
96
+
97
+ [common_chars_of_shorter, common_chars_of_longer].map(&:compact)
98
+ end
99
+ # rubocop:enable Metrics/MethodLength
100
+
101
+ def limited_common_prefix_length
102
+ length = common_prefix_length
103
+
104
+ if length > MAX_COMMON_PREFIX_LENGTH
105
+ MAX_COMMON_PREFIX_LENGTH
106
+ else
107
+ length
108
+ end
109
+ end
110
+
111
+ def matching_index_range(origin)
112
+ min = origin - matching_window
113
+ min = 0 if min < 0
114
+
115
+ max = origin + matching_window
116
+
117
+ min..max
118
+ end
119
+
120
+ def matching_window
121
+ @matching_window ||= (@longer.size / 2).to_i - 1
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -1,6 +1,6 @@
1
1
  module VagrantPlugins
2
2
  # Defines the current plugin version
3
3
  module DevCommands
4
- VERSION = '0.8.0'.freeze
4
+ VERSION = '0.9.0'.freeze
5
5
  end
6
6
  end
data/locales/en.yml CHANGED
@@ -7,7 +7,6 @@ en:
7
7
  Display the help of the command given as the first argument if defined.
8
8
  Just like this help for the help command!
9
9
  usage: "Usage: %{what}"
10
-
11
10
  version:
12
11
  desc: "get currently installed plugin version"
13
12
  help: "Displays the currently installed version of the plugin you are using right now."
@@ -15,6 +14,10 @@ en:
15
14
  message:
16
15
  chain_no_help: "No detailed help for this chain available."
17
16
  command_no_help: "No detailed help for this command available."
17
+ deprecated_box_config: |-
18
+ At least one of your commands is configured with the deprecated
19
+ option ':box'. This option has been renamed to ':machine' and will
20
+ stop working in a future version. Please update your Commandfile.
18
21
  missing_commandfile: "Missing Commandfile"
19
22
  no_commands: "No commands defined!"
20
23
  plugin_readme: |-
@@ -24,7 +27,7 @@ en:
24
27
  A copy of this file should be locally available at:
25
28
  >>> %{readme}
26
29
  plugin_usage: |-
27
- Usage: vagrant run [box] <command>
30
+ Usage: vagrant run [machine] <command>
28
31
  Help: vagrant run help <command>
29
32
 
30
33
  registry:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-devcommands
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Neudert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-19 00:00:00.000000000 Z
11
+ date: 2017-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coveralls
@@ -95,6 +95,7 @@ files:
95
95
  - lib/vagrant/devcommands/runner/internal_command.rb
96
96
  - lib/vagrant/devcommands/synopsis.rb
97
97
  - lib/vagrant/devcommands/util.rb
98
+ - lib/vagrant/devcommands/util/jaro_winkler.rb
98
99
  - lib/vagrant/devcommands/version.rb
99
100
  - locales/en.yml
100
101
  homepage: https://github.com/mneudert/vagrant-devcommands