mixlib-cli 2.0.6 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mixlib/cli.rb +135 -30
- data/lib/mixlib/cli/formatter.rb +32 -0
- data/lib/mixlib/cli/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe2f048e8780700eefac7a4cdf7f7208e5026e46fccd8ba1d174d0ac2392c789
|
4
|
+
data.tar.gz: f16971b7985e2ed441ae186f1aecb8decf8b3e230912fd3690e00b2c3d5ff42b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae94cbf04f14035a78a90ed4a76b2682cc3a59c794c2b7df3148d38946a7fead9cb5bb65d95599b34fdcc215b513441393b4f3b382606d9675088a418b7f6e16
|
7
|
+
data.tar.gz: 6b922bc226625b74d7a2ad3b998a388a0347ac8e0438a6e7c563b98e43d9fb7cc22589c2eca3a398dad99567d8573c2a2725de37edde5f942a505c8b834878a0
|
data/lib/mixlib/cli.rb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require "optparse"
|
20
|
-
|
20
|
+
require "mixlib/cli/formatter"
|
21
21
|
module Mixlib
|
22
22
|
|
23
23
|
# == Mixlib::CLI
|
@@ -30,8 +30,9 @@ module Mixlib
|
|
30
30
|
# === DSL
|
31
31
|
# When included, Mixlib::CLI also extends the including class with its
|
32
32
|
# ClassMethods, which define the DSL. The primary methods of the DSL are
|
33
|
-
# ClassMethods#option, which defines a command line option
|
34
|
-
# ClassMethods#banner, which defines the "usage" banner
|
33
|
+
# ClassMethods#option, which defines a command line option;
|
34
|
+
# ClassMethods#banner, which defines the "usage" banner;
|
35
|
+
# and ClassMethods#deprecated_option, which defines a deprecated command-line option.
|
35
36
|
#
|
36
37
|
# === Parsing
|
37
38
|
# Command line options are parsed by calling the instance method
|
@@ -93,14 +94,88 @@ module Mixlib
|
|
93
94
|
# === Parameters
|
94
95
|
# name<Symbol>:: The name of the option to add
|
95
96
|
# args<Hash>:: A hash of arguments for the option, specifying how it should be parsed.
|
96
|
-
#
|
97
|
-
#
|
97
|
+
# Supported arguments:
|
98
|
+
# :short - The short option, just like from optparse. Example: "-l LEVEL"
|
99
|
+
# :long - The long option, just like from optparse. Example: "--level LEVEL"
|
100
|
+
# :description - The description for this item, just like from optparse.
|
101
|
+
# :default - A default value for this option. Default values will be populated
|
102
|
+
# on parse into `config` or `default_default`, depending `use_separate_defaults`
|
103
|
+
# :boolean - indicates the flag is a boolean. You can use this if the flag takes no arguments
|
104
|
+
# The config value will be set to 'true' if the flag is provided on the CLI and this
|
105
|
+
# argument is set to true. The config value will be set to false only
|
106
|
+
# if it has a default value of false
|
107
|
+
# :required - When set, the option is required. If the command is run without this option,
|
108
|
+
# it will print a message informing the user of the missing requirement, and exit. Default is false.
|
109
|
+
# :proc - Proc that will be invoked if the human has specified this option.
|
110
|
+
# Two forms are supported:
|
111
|
+
# Proc/1 - provided value is passed in.
|
112
|
+
# Proc/2 - first argument is provided value. Second is the cli flag option hash.
|
113
|
+
# Both versions return the value to be assigned to the option.
|
114
|
+
# :show_options - this option is designated as one that shows all supported options/help when invoked.
|
115
|
+
# :exit - exit your program with the exit code when this option is given. Example: 0
|
116
|
+
# :in - array containing a list of valid values. The value provided at run-time for the option is
|
117
|
+
# validated against this. If it is not in the list, it will print a message and exit.
|
118
|
+
# :on :head OR :tail - force this option to display at the beginning or end of the
|
119
|
+
# option list, respectively
|
120
|
+
# =
|
121
|
+
# @return <Hash> :: the config hash for the created option
|
122
|
+
# i
|
98
123
|
def option(name, args)
|
99
124
|
@options ||= {}
|
100
125
|
raise(ArgumentError, "Option name must be a symbol") unless name.kind_of?(Symbol)
|
101
126
|
@options[name.to_sym] = args
|
102
127
|
end
|
103
128
|
|
129
|
+
# Declare a deprecated option
|
130
|
+
#
|
131
|
+
# Add a deprecated command line option.
|
132
|
+
#
|
133
|
+
# name<Symbol> :: The name of the deprecated option
|
134
|
+
# replacement<Symbol> :: The name of the option that replaces this option.
|
135
|
+
# long<String> :: The original long flag name, or flag name with argument, eg "--user USER"
|
136
|
+
# short<String> :: The original short-form flag name, eg "-u USER"
|
137
|
+
# boolean<String> :: true if this is a boolean flag, eg "--[no-]option".
|
138
|
+
# value_mapper<Proc/1> :: a block that accepts the original value from the deprecated option,
|
139
|
+
# and converts it to a value suitable for the new option.
|
140
|
+
# If not provided, the value provided to the deprecated option will be
|
141
|
+
# assigned directly to the converted option.
|
142
|
+
# keep<Boolean> :: Defaults to true, this ensure sthat `options[:deprecated_flag]` is
|
143
|
+
# populated when the deprecated flag is used. If set to false,
|
144
|
+
# only the value in `replacement` will be set. Results undefined
|
145
|
+
# if no replacement is provided. You can use this to enforce the transition
|
146
|
+
# to non-deprecated keys in your code.
|
147
|
+
#
|
148
|
+
# === Returns
|
149
|
+
# <Hash> :: The config hash for the created option.
|
150
|
+
def deprecated_option(name,
|
151
|
+
replacement: nil,
|
152
|
+
long: nil,
|
153
|
+
short: nil,
|
154
|
+
boolean: false,
|
155
|
+
value_mapper: nil,
|
156
|
+
keep: true)
|
157
|
+
|
158
|
+
description = if replacement
|
159
|
+
replacement_cfg = options[replacement]
|
160
|
+
display_name = CLI::Formatter.combined_option_display_name(replacement_cfg[:short], replacement_cfg[:long])
|
161
|
+
"This flag is deprecated. Use #{display_name} instead."
|
162
|
+
else
|
163
|
+
"This flag is deprecated and will be removed in a future release."
|
164
|
+
end
|
165
|
+
value_mapper ||= Proc.new { |v| v }
|
166
|
+
|
167
|
+
option(name,
|
168
|
+
long: long,
|
169
|
+
short: short,
|
170
|
+
boolean: boolean,
|
171
|
+
description: description,
|
172
|
+
on: :tail,
|
173
|
+
deprecated: true,
|
174
|
+
keep: keep,
|
175
|
+
replacement: replacement,
|
176
|
+
value_mapper: value_mapper)
|
177
|
+
end
|
178
|
+
|
104
179
|
# Get the hash of current options.
|
105
180
|
#
|
106
181
|
# === Returns
|
@@ -209,7 +284,6 @@ module Mixlib
|
|
209
284
|
config_opts[:show_options] ||= false
|
210
285
|
config_opts[:exit] ||= nil
|
211
286
|
config_opts[:in] ||= nil
|
212
|
-
|
213
287
|
if config_opts.key?(:default)
|
214
288
|
defaults_container[config_key] = config_opts[:default]
|
215
289
|
end
|
@@ -225,25 +299,30 @@ module Mixlib
|
|
225
299
|
#
|
226
300
|
# === Returns
|
227
301
|
# argv<Array>:: Returns any un-parsed elements.
|
228
|
-
def parse_options(argv = ARGV)
|
302
|
+
def parse_options(argv = ARGV, show_deprecations: true)
|
229
303
|
argv = argv.dup
|
230
304
|
opt_parser.parse!(argv)
|
305
|
+
# Do this before our custom validations, so that custom
|
306
|
+
# validations apply to any converted deprecation values;
|
307
|
+
# but after parse! so that everything is populated.
|
308
|
+
handle_deprecated_options(show_deprecations)
|
231
309
|
|
232
310
|
# Deal with any required values
|
233
|
-
options.each do |opt_key,
|
234
|
-
if
|
235
|
-
reqarg =
|
311
|
+
options.each do |opt_key, opt_config|
|
312
|
+
if opt_config[:required] && !config.key?(opt_key)
|
313
|
+
reqarg = opt_config[:short] || opt_config[:long]
|
236
314
|
puts "You must supply #{reqarg}!"
|
237
315
|
puts @opt_parser
|
238
316
|
exit 2
|
239
317
|
end
|
240
|
-
if
|
241
|
-
unless
|
318
|
+
if opt_config[:in]
|
319
|
+
unless opt_config[:in].kind_of?(Array)
|
242
320
|
raise(ArgumentError, "Options config key :in must receive an Array")
|
243
321
|
end
|
244
|
-
if config[opt_key] && !
|
245
|
-
reqarg =
|
246
|
-
puts "#{reqarg}: #{config[opt_key]} is not one of the allowed values: #{friendly_opt_list(
|
322
|
+
if config[opt_key] && !opt_config[:in].include?(config[opt_key])
|
323
|
+
reqarg = Formatter.combined_option_display_name(opt_config[:short], opt_config[:long])
|
324
|
+
puts "#{reqarg}: #{config[opt_key]} is not one of the allowed values: #{Formatter.friendly_opt_list(opt_config[:in])}"
|
325
|
+
# TODO - get rid of this. nobody wants to be spammed with a ton of information, particularly since we just told them the exact problem and how to fix it.
|
247
326
|
puts @opt_parser
|
248
327
|
exit 2
|
249
328
|
end
|
@@ -267,7 +346,6 @@ module Mixlib
|
|
267
346
|
# Create new options
|
268
347
|
options.sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |opt_key, opt_val|
|
269
348
|
opt_args = build_option_arguments(opt_val)
|
270
|
-
|
271
349
|
opt_method = case opt_val[:on]
|
272
350
|
when :on
|
273
351
|
:on
|
@@ -305,15 +383,53 @@ module Mixlib
|
|
305
383
|
end
|
306
384
|
end
|
307
385
|
|
386
|
+
# Iterates through options declared as deprecated,
|
387
|
+
# maps values to their replacement options,
|
388
|
+
# and prints deprecation warnings.
|
389
|
+
#
|
390
|
+
# @return NilClass
|
391
|
+
def handle_deprecated_options(show_deprecations)
|
392
|
+
merge_in_values = {}
|
393
|
+
config.each_key do |opt_key|
|
394
|
+
opt_cfg = options[opt_key]
|
395
|
+
# Deprecated entries do not have defaults so no matter what
|
396
|
+
# separate_default_options are set, if we see a 'config'
|
397
|
+
# entry that contains a deprecated indicator, then the option was
|
398
|
+
# explicitly provided by the caller.
|
399
|
+
next unless opt_cfg[:deprecated]
|
400
|
+
replacement_key = opt_cfg[:replacement]
|
401
|
+
if replacement_key
|
402
|
+
# This is the value passed into the deprecated flag. We'll use
|
403
|
+
# the declared value mapper (defaults to return the same value if caller hasn't
|
404
|
+
# provided a mapper).
|
405
|
+
deprecated_val = config[opt_key]
|
406
|
+
|
407
|
+
# We can't modify 'config' since we're iterating it, apply updates
|
408
|
+
# at the end.
|
409
|
+
merge_in_values[replacement_key] = opt_cfg[:value_mapper].call(deprecated_val)
|
410
|
+
config.delete(opt_key) unless opt_cfg[:keep]
|
411
|
+
end
|
412
|
+
|
413
|
+
# Warn about the deprecation.
|
414
|
+
if show_deprecations
|
415
|
+
# Description is also the deprecation message.
|
416
|
+
display_name = CLI::Formatter.combined_option_display_name(opt_cfg[:short], opt_cfg[:long])
|
417
|
+
puts "#{display_name}: #{opt_cfg[:description]}"
|
418
|
+
end
|
419
|
+
end
|
420
|
+
config.merge!(merge_in_values)
|
421
|
+
nil
|
422
|
+
end
|
423
|
+
|
308
424
|
def build_option_arguments(opt_setting)
|
309
425
|
arguments = Array.new
|
310
426
|
|
311
|
-
arguments << opt_setting[:short] if opt_setting
|
312
|
-
arguments << opt_setting[:long] if opt_setting
|
427
|
+
arguments << opt_setting[:short] if opt_setting[:short]
|
428
|
+
arguments << opt_setting[:long] if opt_setting[:long]
|
313
429
|
if opt_setting.key?(:description)
|
314
430
|
description = opt_setting[:description].dup
|
315
431
|
description << " (required)" if opt_setting[:required]
|
316
|
-
description << " (valid options: #{friendly_opt_list(opt_setting[:in])})" if opt_setting[:in]
|
432
|
+
description << " (valid options: #{Formatter.friendly_opt_list(opt_setting[:in])})" if opt_setting[:in]
|
317
433
|
opt_setting[:description] = description
|
318
434
|
arguments << description
|
319
435
|
end
|
@@ -321,20 +437,9 @@ module Mixlib
|
|
321
437
|
arguments
|
322
438
|
end
|
323
439
|
|
324
|
-
# @private
|
325
|
-
# @param opt_arry [Array]
|
326
|
-
#
|
327
|
-
# @return [String] a friendly quoted list of items complete with "or"
|
328
|
-
def friendly_opt_list(opt_array)
|
329
|
-
opts = opt_array.map { |x| "'#{x}'" }
|
330
|
-
return opts.join(" or ") if opts.size < 3
|
331
|
-
opts[0..-2].join(", ") + ", or " + opts[-1]
|
332
|
-
end
|
333
|
-
|
334
440
|
def self.included(receiver)
|
335
441
|
receiver.extend(Mixlib::CLI::ClassMethods)
|
336
442
|
receiver.extend(Mixlib::CLI::InheritMethods)
|
337
443
|
end
|
338
|
-
|
339
444
|
end
|
340
445
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Mixlib
|
3
|
+
module CLI
|
4
|
+
class Formatter
|
5
|
+
# Create a string that includes both versions (short/long) of a flag name
|
6
|
+
# based on on whether short/long/both/neither are provided
|
7
|
+
#
|
8
|
+
# @param short [String] the short name of the option. Can be nil.
|
9
|
+
# @param long [String] the long name of the option. Can be nil.
|
10
|
+
# @return [String] the formatted flag name as described above
|
11
|
+
def self.combined_option_display_name(short, long)
|
12
|
+
usage = ""
|
13
|
+
# short/long may have an argument (--long ARG)
|
14
|
+
# splitting on " " and taking first ensures that we get just
|
15
|
+
# the flag name without the argument if one is present.
|
16
|
+
usage << short.split(" ").first if short
|
17
|
+
usage << "/" if long && short
|
18
|
+
usage << long.split(" ").first if long
|
19
|
+
usage
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param opt_arry [Array]
|
23
|
+
#
|
24
|
+
# @return [String] a friendly quoted list of items complete with "or"
|
25
|
+
def self.friendly_opt_list(opt_array)
|
26
|
+
opts = opt_array.map { |x| "'#{x}'" }
|
27
|
+
return opts.join(" or ") if opts.size < 3
|
28
|
+
opts[0..-2].join(", ") + ", or " + opts[-1]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/mixlib/cli/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixlib-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef Software, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A simple mixin for CLI interfaces, including option parsing
|
14
14
|
email: info@chef.io
|
@@ -19,8 +19,9 @@ files:
|
|
19
19
|
- LICENSE
|
20
20
|
- NOTICE
|
21
21
|
- lib/mixlib/cli.rb
|
22
|
+
- lib/mixlib/cli/formatter.rb
|
22
23
|
- lib/mixlib/cli/version.rb
|
23
|
-
homepage: https://
|
24
|
+
homepage: https://github.com/chef/mixlib-cli
|
24
25
|
licenses:
|
25
26
|
- Apache-2.0
|
26
27
|
metadata: {}
|