aspera-cli 4.12.0 → 4.14.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +45 -5
- data/CONTRIBUTING.md +113 -22
- data/README.md +1289 -754
- data/bin/ascli +3 -3
- data/examples/dascli +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +63 -74
- data/lib/aspera/ascmd.rb +5 -3
- data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
- data/lib/aspera/cli/extended_value.rb +24 -37
- data/lib/aspera/cli/formatter.rb +23 -25
- data/lib/aspera/cli/info.rb +2 -4
- data/lib/aspera/cli/main.rb +27 -27
- data/lib/aspera/cli/manager.rb +143 -120
- data/lib/aspera/cli/plugin.rb +88 -43
- data/lib/aspera/cli/plugins/alee.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +235 -104
- data/lib/aspera/cli/plugins/ats.rb +16 -18
- data/lib/aspera/cli/plugins/bss.rb +3 -3
- data/lib/aspera/cli/plugins/config.rb +190 -373
- data/lib/aspera/cli/plugins/console.rb +4 -6
- data/lib/aspera/cli/plugins/cos.rb +12 -13
- data/lib/aspera/cli/plugins/faspex.rb +21 -21
- data/lib/aspera/cli/plugins/faspex5.rb +399 -150
- data/lib/aspera/cli/plugins/node.rb +260 -174
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
- data/lib/aspera/cli/plugins/preview.rb +40 -62
- data/lib/aspera/cli/plugins/server.rb +33 -16
- data/lib/aspera/cli/plugins/shares.rb +24 -33
- data/lib/aspera/cli/plugins/sync.rb +6 -6
- data/lib/aspera/cli/transfer_agent.rb +47 -30
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +9 -7
- data/lib/aspera/command_line_builder.rb +2 -1
- data/lib/aspera/cos_node.rb +1 -1
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +7 -3
- data/lib/aspera/fasp/agent_connect.rb +6 -1
- data/lib/aspera/fasp/agent_direct.rb +17 -17
- data/lib/aspera/fasp/agent_httpgw.rb +138 -60
- data/lib/aspera/fasp/agent_node.rb +14 -4
- data/lib/aspera/fasp/agent_trsdk.rb +2 -0
- data/lib/aspera/fasp/error_info.rb +2 -0
- data/lib/aspera/fasp/installation.rb +19 -19
- data/lib/aspera/fasp/parameters.rb +29 -20
- data/lib/aspera/fasp/parameters.yaml +5 -2
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +8 -5
- data/lib/aspera/fasp/uri.rb +23 -21
- data/lib/aspera/faspex_gw.rb +1 -0
- data/lib/aspera/faspex_postproc.rb +3 -3
- data/lib/aspera/hash_ext.rb +12 -2
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/log.rb +1 -0
- data/lib/aspera/node.rb +73 -84
- data/lib/aspera/oauth.rb +4 -3
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/preview/file_types.rb +8 -6
- data/lib/aspera/preview/generator.rb +23 -11
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +80 -0
- data/lib/aspera/preview/utils.rb +11 -11
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +42 -4
- data/lib/aspera/rest_call_error.rb +3 -1
- data/lib/aspera/secret_hider.rb +10 -5
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/sync.rb +41 -33
- data/lib/aspera/web_server_simple.rb +22 -18
- data.tar.gz.sig +0 -0
- metadata +40 -48
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -179
- data/examples/aoc.rb +0 -30
- data/examples/faspex4.rb +0 -94
- data/examples/node.rb +0 -96
- data/examples/server.rb +0 -93
- data/lib/aspera/data/7 +0 -0
data/lib/aspera/cli/main.rb
CHANGED
@@ -31,7 +31,7 @@ module Aspera
|
|
31
31
|
STATUS_FIELD = 'status'
|
32
32
|
|
33
33
|
# for testing only
|
34
|
-
SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse)
|
34
|
+
SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse) # cspell: disable-line
|
35
35
|
|
36
36
|
class << self
|
37
37
|
# expect some list, but nothing to display
|
@@ -87,7 +87,7 @@ module Aspera
|
|
87
87
|
if @option_insecure
|
88
88
|
url = http.inspect.gsub(/^[^ ]* /, 'https://').gsub(/ [^ ]*$/, '')
|
89
89
|
if !@ssl_warned_urls.include?(url)
|
90
|
-
@formatter.display_message(:error, "#{WARNING_FLASH} ignoring certificate for: #{url}. Do not
|
90
|
+
@formatter.display_message(:error, "#{WARNING_FLASH} ignoring certificate for: #{url}. Do not deactivate certificate verification in production.")
|
91
91
|
@ssl_warned_urls.push(url)
|
92
92
|
end
|
93
93
|
http.verify_mode = SELF_SIGNED_CERT
|
@@ -187,31 +187,25 @@ module Aspera
|
|
187
187
|
# define header for manual
|
188
188
|
def init_global_options
|
189
189
|
Log.log.debug('init_global_options')
|
190
|
-
@opt_mgr.
|
191
|
-
@opt_mgr.
|
192
|
-
@opt_mgr.
|
193
|
-
@opt_mgr.
|
194
|
-
@opt_mgr.
|
195
|
-
@opt_mgr.
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
@opt_mgr.
|
202
|
-
@opt_mgr.
|
203
|
-
@opt_mgr.
|
204
|
-
@opt_mgr.
|
205
|
-
@opt_mgr.
|
206
|
-
@opt_mgr.
|
207
|
-
@opt_mgr.
|
208
|
-
@opt_mgr.
|
209
|
-
@opt_mgr.add_opt_boolean(:insecure, 'do not validate HTTPS certificate')
|
210
|
-
@opt_mgr.add_opt_boolean(:once_only, 'process only new items (some commands)')
|
211
|
-
@opt_mgr.add_opt_boolean(:log_secrets, 'show passwords in logs')
|
212
|
-
@opt_mgr.add_opt_boolean(:cache_tokens, 'save and reuse Oauth tokens')
|
213
|
-
@opt_mgr.set_option(:ui, OpenApplication.default_gui_mode)
|
214
|
-
@opt_mgr.set_option(:once_only, false)
|
190
|
+
@opt_mgr.declare(:help, 'Show this message', values: :none, short: 'h') { @option_help = true }
|
191
|
+
@opt_mgr.declare(:bash_comp, 'Generate bash completion for command', values: :none) { @bash_completion = true }
|
192
|
+
@opt_mgr.declare(:show_config, 'Display parameters used for the provided action', values: :none) { @option_show_config = true }
|
193
|
+
@opt_mgr.declare(:rest_debug, 'More debug for HTTP calls (REST)', values: :none, short: 'r') { @option_rest_debug = true }
|
194
|
+
@opt_mgr.declare(:version, 'Display version', values: :none, short: 'v') { @formatter.display_message(:data, Aspera::Cli::VERSION); Process.exit(0) } # rubocop:disable Style/Semicolon, Layout/LineLength
|
195
|
+
@opt_mgr.declare(:warnings, 'Check for language warnings', values: :none, short: 'w') { $VERBOSE = true }
|
196
|
+
@opt_mgr.declare(
|
197
|
+
:ui, 'Method to start browser',
|
198
|
+
values: OpenApplication.user_interfaces,
|
199
|
+
handler: {o: self, m: :option_ui},
|
200
|
+
default: OpenApplication.default_gui_mode)
|
201
|
+
@opt_mgr.declare(:log_level, 'Log level', values: Log.levels, handler: {o: Log.instance, m: :level})
|
202
|
+
@opt_mgr.declare(:logger, 'Logging method', values: Log::LOG_TYPES, handler: {o: Log.instance, m: :logger_type})
|
203
|
+
@opt_mgr.declare(:lock_port, 'Prevent dual execution of a command, e.g. in cron')
|
204
|
+
@opt_mgr.declare(:http_options, 'Options for http socket', types: Hash, handler: {o: self, m: :option_http_options})
|
205
|
+
@opt_mgr.declare(:insecure, 'Do not validate HTTPS certificate', values: :bool, handler: {o: self, m: :option_insecure}, default: :no)
|
206
|
+
@opt_mgr.declare(:once_only, 'Process only new items (some commands)', values: :bool, default: false)
|
207
|
+
@opt_mgr.declare(:log_secrets, 'Show passwords in logs', values: :bool, handler: {o: SecretHider, m: :log_secrets})
|
208
|
+
@opt_mgr.declare(:cache_tokens, 'Save and reuse Oauth tokens', values: :bool, handler: {o: self, m: :option_cache_tokens})
|
215
209
|
# parse declared options
|
216
210
|
@opt_mgr.parse_options!
|
217
211
|
end
|
@@ -335,6 +329,7 @@ module Aspera
|
|
335
329
|
# finish
|
336
330
|
@plugin_env[:transfer].shutdown
|
337
331
|
rescue Net::SSH::AuthenticationFailed => e; exception_info = {e: e, t: 'SSH', security: true}
|
332
|
+
rescue OpenSSL::SSL::SSLError => e; exception_info = {e: e, t: 'SSL'}
|
338
333
|
rescue CliBadArgument => e; exception_info = {e: e, t: 'Argument', usage: true}
|
339
334
|
rescue CliNoSuchId => e; exception_info = {e: e, t: 'Identifier'}
|
340
335
|
rescue CliError => e; exception_info = {e: e, t: 'Tool', usage: true}
|
@@ -351,10 +346,15 @@ module Aspera
|
|
351
346
|
Log.log.warn(exception_info[:e].message) if Aspera::Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
|
352
347
|
@formatter.display_message(:error, "#{ERROR_FLASH} #{exception_info[:t]}: #{exception_info[:e].message}")
|
353
348
|
@formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
|
349
|
+
# Provide hint on FASP errors
|
354
350
|
if exception_info[:e].is_a?(Fasp::Error) && exception_info[:e].message.eql?('Remote host is not who we expected')
|
355
351
|
@formatter.display_message(:error, "For this specific error, refer to:\n"\
|
356
352
|
"#{SRC_URL}#error-remote-host-is-not-who-we-expected\nAdd this to arguments:\n--ts=@json:'{\"sshfp\":null}'")
|
357
353
|
end
|
354
|
+
# Provide hint on SSL errors
|
355
|
+
if exception_info[:e].is_a?(OpenSSL::SSL::SSLError) && ['does not match the server certificate'].any?{|m|exception_info[:e].message.include?(m)}
|
356
|
+
@formatter.display_message(:error, "You can ignore SSL errors with option:\n--insecure=yes")
|
357
|
+
end
|
358
358
|
end
|
359
359
|
# 2- processing of command not processed (due to exception or bad command line)
|
360
360
|
if execute_command || @option_show_config
|
data/lib/aspera/cli/manager.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'aspera/colors'
|
4
4
|
require 'aspera/log'
|
5
|
+
require 'aspera/secret_hider'
|
5
6
|
require 'aspera/cli/extended_value'
|
6
7
|
require 'optparse'
|
7
8
|
require 'io/console'
|
@@ -52,9 +53,9 @@ module Aspera
|
|
52
53
|
# option name separator on command line
|
53
54
|
OPTION_SEP_LINE = '-'
|
54
55
|
# option name separator in code (symbol)
|
55
|
-
|
56
|
+
OPTION_SEP_SYMB = '_'
|
56
57
|
|
57
|
-
private_constant :FALSE_VALUES, :TRUE_VALUES, :BOOLEAN_VALUES, :OPTION_SEP_LINE, :
|
58
|
+
private_constant :FALSE_VALUES, :TRUE_VALUES, :BOOLEAN_VALUES, :OPTION_SEP_LINE, :OPTION_SEP_SYMB
|
58
59
|
|
59
60
|
class << self
|
60
61
|
def enum_to_bool(enum)
|
@@ -81,6 +82,15 @@ module Aspera
|
|
81
82
|
def bad_arg_message_multi(error_msg, choices)
|
82
83
|
return [error_msg, 'Use:'].concat(choices.map{|c|"- #{c}"}.sort).join("\n")
|
83
84
|
end
|
85
|
+
|
86
|
+
# change option name with dash to name with underscore
|
87
|
+
def option_line_to_name(name)
|
88
|
+
return name.gsub(OPTION_SEP_LINE, OPTION_SEP_SYMB)
|
89
|
+
end
|
90
|
+
|
91
|
+
def option_name_to_line(name)
|
92
|
+
return "--#{name.to_s.gsub(OPTION_SEP_SYMB, OPTION_SEP_LINE)}"
|
93
|
+
end
|
84
94
|
end
|
85
95
|
|
86
96
|
attr_reader :parser
|
@@ -94,7 +104,7 @@ module Aspera
|
|
94
104
|
@unprocessed_cmd_line_options = []
|
95
105
|
# a copy of all initial options
|
96
106
|
@initial_cli_options = []
|
97
|
-
# option description: key = option symbol, value=hash, :
|
107
|
+
# option description: key = option symbol, value=hash, :read_write, :accessor, :value, :accepted
|
98
108
|
@declared_options = {}
|
99
109
|
# do we ask missing options and arguments to user ?
|
100
110
|
@ask_missing_mandatory = false # STDIN.isatty
|
@@ -108,7 +118,7 @@ module Aspera
|
|
108
118
|
@parser = OptionParser.new
|
109
119
|
@parser.program_name = program_name
|
110
120
|
# options can also be provided by env vars : --param-name -> ASCLI_PARAM_NAME
|
111
|
-
env_prefix = program_name.upcase +
|
121
|
+
env_prefix = program_name.upcase + OPTION_SEP_SYMB
|
112
122
|
ENV.each do |k, v|
|
113
123
|
if k.start_with?(env_prefix)
|
114
124
|
@unprocessed_env.push([k[env_prefix.length..-1].downcase.to_sym, v])
|
@@ -121,10 +131,8 @@ module Aspera
|
|
121
131
|
unless argv.nil?
|
122
132
|
@parser.separator('')
|
123
133
|
@parser.separator('OPTIONS: global')
|
124
|
-
|
125
|
-
|
126
|
-
add_opt_boolean(:interactive, 'use interactive input of missing params')
|
127
|
-
add_opt_boolean(:ask_options, 'ask even optional options')
|
134
|
+
declare(:interactive, 'Use interactive input of missing params', values: :bool, handler: {o: self, m: :ask_missing_mandatory})
|
135
|
+
declare(:ask_options, 'Ask even optional options', values: :bool, handler: {o: self, m: :ask_missing_optional})
|
128
136
|
parse_options!
|
129
137
|
process_options = true
|
130
138
|
until argv.empty?
|
@@ -144,21 +152,20 @@ module Aspera
|
|
144
152
|
Log.log.debug{"add_cmd_line_options:commands/args=#{@unprocessed_cmd_line_arguments},options=#{@unprocessed_cmd_line_options}".red}
|
145
153
|
end
|
146
154
|
|
147
|
-
def get_next_command(command_list); return get_next_argument('command', expected: command_list); end
|
148
|
-
|
149
155
|
# @param expected is
|
150
156
|
# - Array of allowed value (single value)
|
151
157
|
# - :multiple for remaining values
|
152
158
|
# - :single for a single unconstrained value
|
153
159
|
# @param mandatory true/false
|
154
160
|
# @param type expected class for result
|
161
|
+
# @param aliases list of aliases for the value
|
155
162
|
# @return value, list or nil
|
156
|
-
def get_next_argument(descr, expected: :single, mandatory: true, type: nil)
|
163
|
+
def get_next_argument(descr, expected: :single, mandatory: true, type: nil, aliases: nil, default: nil)
|
157
164
|
unless type.nil?
|
158
165
|
raise 'internal: type must be a Class' unless type.is_a?(Class)
|
159
166
|
descr = "#{descr} (#{type})"
|
160
167
|
end
|
161
|
-
result =
|
168
|
+
result = default
|
162
169
|
if !@unprocessed_cmd_line_arguments.empty?
|
163
170
|
# there are values
|
164
171
|
case expected
|
@@ -170,66 +177,37 @@ module Aspera
|
|
170
177
|
if result.length.eql?(1) && result.first.is_a?(Array)
|
171
178
|
result = result.first
|
172
179
|
end
|
180
|
+
when Array
|
181
|
+
allowed_values = [].concat(expected)
|
182
|
+
allowed_values.concat(aliases.keys) unless aliases.nil?
|
183
|
+
raise "internal error: only symbols allowed: #{allowed_values}" unless allowed_values.all?(Symbol)
|
184
|
+
result = self.class.get_from_list(@unprocessed_cmd_line_arguments.shift, descr, allowed_values)
|
173
185
|
else
|
174
|
-
|
186
|
+
raise 'internal error'
|
175
187
|
end
|
176
188
|
elsif mandatory
|
177
189
|
# no value provided
|
178
190
|
result = get_interactive(:argument, descr, expected: expected)
|
179
191
|
end
|
180
192
|
Log.log.debug{"#{descr}=#{result}"}
|
193
|
+
result = aliases[result] if !aliases.nil? && aliases.key?(result)
|
181
194
|
raise "argument shall be #{type.name}" unless type.nil? || result.is_a?(type)
|
182
195
|
return result
|
183
196
|
end
|
184
197
|
|
185
|
-
|
186
|
-
def declare_option(option_symbol, type)
|
187
|
-
Log.log.debug{"declare_option: #{option_symbol}: #{type}: skip=#{@declared_options.key?(option_symbol)}".green}
|
188
|
-
if @declared_options.key?(option_symbol)
|
189
|
-
raise "INTERNAL ERROR: option #{option_symbol} already declared. only accessor can be re-declared and ignored" \
|
190
|
-
unless @declared_options[option_symbol][:type].eql?(:accessor)
|
191
|
-
return
|
192
|
-
end
|
193
|
-
@declared_options[option_symbol] = {type: type}
|
194
|
-
# by default passwords and secrets are sensitive, else specify when declaring the option
|
195
|
-
@declared_options[option_symbol][:sensitive] = true if !%w[password secret key].select{|i| option_symbol.to_s.end_with?(i)}.empty?
|
196
|
-
end
|
197
|
-
|
198
|
-
# define option with handler
|
199
|
-
def set_obj_attr(option_symbol, object, attr_symb, default_value=nil)
|
200
|
-
Log.log.debug{"set attr obj #{option_symbol} (#{object},#{attr_symb})"}
|
201
|
-
declare_option(option_symbol, :accessor)
|
202
|
-
@declared_options[option_symbol][:accessor] = AttrAccessor.new(object, attr_symb)
|
203
|
-
set_option(option_symbol, default_value, 'default obj attr') if !default_value.nil?
|
204
|
-
end
|
205
|
-
|
206
|
-
# set an option value by name, either store value or call handler
|
207
|
-
def set_option(option_symbol, value, where='default')
|
208
|
-
if !@declared_options.key?(option_symbol)
|
209
|
-
Log.log.debug{"set unknown option: #{option_symbol}"}
|
210
|
-
raise 'ERROR: cannot set undeclared option'
|
211
|
-
# declare_option(option_symbol)
|
212
|
-
end
|
213
|
-
value = ExtendedValue.instance.evaluate(value)
|
214
|
-
value = Manager.enum_to_bool(value) if @declared_options[option_symbol][:values].eql?(BOOLEAN_VALUES)
|
215
|
-
Log.log.debug{"(#{@declared_options[option_symbol][:type]}/#{where}) set #{option_symbol}=#{value}"}
|
216
|
-
case @declared_options[option_symbol][:type]
|
217
|
-
when :accessor
|
218
|
-
@declared_options[option_symbol][:accessor].value = value
|
219
|
-
when :value
|
220
|
-
@declared_options[option_symbol][:value] = value
|
221
|
-
else # nil or other
|
222
|
-
raise 'error'
|
223
|
-
end
|
224
|
-
end
|
198
|
+
def get_next_command(command_list, aliases: nil); return get_next_argument('command', expected: command_list, aliases: aliases); end
|
225
199
|
|
226
|
-
#
|
227
|
-
# either return value or
|
200
|
+
# Get an option value by name
|
201
|
+
# either return value or calls handler, can return nil
|
228
202
|
# ask interactively if requested/required
|
229
|
-
|
203
|
+
# @param mandatory [Boolean] if true, raise error if option not set
|
204
|
+
# @param allowed_types [Array] list of allowed types
|
205
|
+
def get_option(option_symbol, mandatory: false, allowed_types: nil)
|
206
|
+
allowed_types = [allowed_types] if allowed_types.is_a?(Class)
|
207
|
+
raise 'Internal Error: allowed_types must be an Array of Class or a Class' unless allowed_types.nil? || allowed_types.is_a?(Array)
|
230
208
|
result = nil
|
231
209
|
if @declared_options.key?(option_symbol)
|
232
|
-
case @declared_options[option_symbol][:
|
210
|
+
case @declared_options[option_symbol][:read_write]
|
233
211
|
when :accessor
|
234
212
|
result = @declared_options[option_symbol][:accessor].value
|
235
213
|
when :value
|
@@ -237,15 +215,15 @@ module Aspera
|
|
237
215
|
else
|
238
216
|
raise 'unknown type'
|
239
217
|
end
|
240
|
-
Log.log.debug{"(#{@declared_options[option_symbol][:
|
218
|
+
Log.log.debug{"(#{@declared_options[option_symbol][:read_write]}) get #{option_symbol}=#{result}"}
|
241
219
|
end
|
242
220
|
# do not fail for manual generation if option mandatory but not set
|
243
|
-
result = '' if result.nil? &&
|
221
|
+
result = '' if result.nil? && mandatory && !@fail_on_missing_mandatory
|
244
222
|
# Log.log.debug{"interactive=#{@ask_missing_mandatory}"}
|
245
223
|
if result.nil?
|
246
224
|
if !@ask_missing_mandatory
|
247
|
-
raise CliBadArgument, "Missing mandatory option: #{option_symbol}" if
|
248
|
-
elsif @ask_missing_optional ||
|
225
|
+
raise CliBadArgument, "Missing mandatory option: #{option_symbol}" if mandatory
|
226
|
+
elsif @ask_missing_optional || mandatory
|
249
227
|
# ask_missing_mandatory
|
250
228
|
expected = :single
|
251
229
|
# print "please enter: #{option_symbol.to_s}"
|
@@ -256,72 +234,117 @@ module Aspera
|
|
256
234
|
set_option(option_symbol, result, 'interactive')
|
257
235
|
end
|
258
236
|
end
|
237
|
+
raise "option #{option_symbol} is #{result.class} but must be one of #{allowed_types}" unless \
|
238
|
+
!mandatory || allowed_types.nil? || allowed_types.any? { |t|result.is_a?(t)}
|
259
239
|
return result
|
260
240
|
end
|
261
241
|
|
262
|
-
#
|
263
|
-
def
|
264
|
-
|
265
|
-
|
266
|
-
#
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
#
|
276
|
-
|
277
|
-
value = get_option(option_symbol)
|
278
|
-
help_values = values.map{|i|i.eql?(value) ? highlight_current(i) : i}.join(', ')
|
279
|
-
if values.eql?(BOOLEAN_VALUES)
|
280
|
-
help_values = BOOLEAN_SIMPLE.map{|i|(i.eql?(:yes) && value) || (i.eql?(:no) && !value) ? highlight_current(i) : i}.join(', ')
|
242
|
+
# set an option value by name, either store value or call handler
|
243
|
+
def set_option(option_symbol, value, where='code override')
|
244
|
+
raise CliBadArgument, "Unknown option: #{option_symbol}" unless @declared_options.key?(option_symbol)
|
245
|
+
attributes = @declared_options[option_symbol]
|
246
|
+
Log.log.warn("#{option_symbol}: Option is deprecated: #{attributes[:deprecation]}") if attributes[:deprecation]
|
247
|
+
value = ExtendedValue.instance.evaluate(value)
|
248
|
+
value = Manager.enum_to_bool(value) if attributes[:values].eql?(BOOLEAN_VALUES)
|
249
|
+
Log.log.debug{"(#{attributes[:read_write]}/#{where}) set #{option_symbol}=#{value}"}
|
250
|
+
case attributes[:read_write]
|
251
|
+
when :accessor
|
252
|
+
attributes[:accessor].value = value
|
253
|
+
when :value
|
254
|
+
attributes[:value] = value
|
255
|
+
else # nil or other
|
256
|
+
raise 'error'
|
281
257
|
end
|
282
|
-
on_args.push(values)
|
283
|
-
on_args.push("#{help}: #{help_values}")
|
284
|
-
Log.log.debug{"on_args=#{on_args}"}
|
285
|
-
@parser.on(*on_args){|v|set_option(option_symbol, self.class.get_from_list(v.to_s, help, values), 'cmdline')}
|
286
|
-
end
|
287
|
-
|
288
|
-
def add_opt_boolean(option_symbol, help, *on_args)
|
289
|
-
add_opt_list(option_symbol, BOOLEAN_VALUES, help, *on_args)
|
290
|
-
# if default was defined for obj, it may still be enum (yes/no) instead of boolean
|
291
|
-
default_value = get_option(option_symbol)
|
292
|
-
set_option(option_symbol, default_value, 'opt boolean') unless default_value.nil?
|
293
|
-
end
|
294
|
-
|
295
|
-
# define an option with open values
|
296
|
-
def add_opt_simple(option_symbol, *on_args)
|
297
|
-
declare_option(option_symbol, :value)
|
298
|
-
Log.log.debug{"add_opt_simple #{option_symbol}"}
|
299
|
-
on_args.unshift(symbol_to_option(option_symbol, 'VALUE'))
|
300
|
-
Log.log.debug{"on_args=#{on_args}"}
|
301
|
-
@parser.on(*on_args) { |v| set_option(option_symbol, v, 'cmdline') }
|
302
258
|
end
|
303
259
|
|
304
|
-
#
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
260
|
+
# declare an option
|
261
|
+
# @param option_symbol [Symbol] option name
|
262
|
+
# @param description [String] description for help
|
263
|
+
# @param handler [Hash] handler for option value: keys: o (object) and m (method)
|
264
|
+
# @param default [Object] default value
|
265
|
+
# @param values [nil, Array, :bool, :date, :none] list of allowed values, :bool for true/false, :date for dates, :none for on/off switch
|
266
|
+
# @param short [String] short option name
|
267
|
+
# @param coerce [Class] one of the coerce types accepted par option parser
|
268
|
+
# @param types [Class, Array] accepted value type(s)
|
269
|
+
# @param block [Proc] block to execute when option is found
|
270
|
+
def declare(option_symbol, description, handler: nil, default: nil, values: nil, short: nil, coerce: nil, types: nil, deprecation: nil, &block)
|
271
|
+
raise "INTERNAL ERROR: #{option_symbol} already declared" if @declared_options.key?(option_symbol)
|
272
|
+
raise "INTERNAL ERROR: #{option_symbol} ends with dot" unless description[-1] != '.'
|
273
|
+
raise "INTERNAL ERROR: #{option_symbol} does not start with capital" unless description[0] == description[0].upcase
|
274
|
+
raise "INTERNAL ERROR: #{option_symbol} shall use :types" if description.downcase.include?('hash') || description.downcase.include?('extended value')
|
275
|
+
opt = @declared_options[option_symbol] = {
|
276
|
+
read_write: handler.nil? ? :value : :accessor,
|
277
|
+
# by default passwords and secrets are sensitive, else specify when declaring the option
|
278
|
+
sensitive: SecretHider.secret?(option_symbol, '')
|
279
|
+
}
|
280
|
+
if !types.nil?
|
281
|
+
types = [types] unless types.is_a?(Array)
|
282
|
+
raise "INTERNAL ERROR: types must be classes: #{types}" unless types.all?(Class)
|
283
|
+
opt[:types] = types
|
284
|
+
description = "#{description} (#{types.map(&:name).join(', ')})"
|
285
|
+
end
|
286
|
+
if deprecation
|
287
|
+
opt[:deprecation] = deprecation
|
288
|
+
description = "#{description} (#{'deprecated'.blue}: #{deprecation})"
|
289
|
+
end
|
290
|
+
Log.log.debug{"declare: #{option_symbol}: #{opt[:read_write]}".green}
|
291
|
+
if opt[:read_write].eql?(:accessor)
|
292
|
+
raise 'internal error' unless handler.is_a?(Hash)
|
293
|
+
raise 'internal error' unless handler.keys.sort.eql?(%i[m o])
|
294
|
+
Log.log.debug{"set attr obj #{option_symbol} (#{handler[:o]},#{handler[:m]})"}
|
295
|
+
opt[:accessor] = AttrAccessor.new(handler[:o], handler[:m])
|
296
|
+
end
|
297
|
+
set_option(option_symbol, default, 'default') unless default.nil?
|
298
|
+
on_args = [description]
|
299
|
+
case values
|
300
|
+
when nil
|
301
|
+
on_args.push(symbol_to_option(option_symbol, 'VALUE'))
|
302
|
+
on_args.push("-#{short}VALUE") unless short.nil?
|
303
|
+
on_args.push(coerce) unless coerce.nil?
|
304
|
+
@parser.on(*on_args) { |v| set_option(option_symbol, v, 'cmdline') }
|
305
|
+
when Array, :bool
|
306
|
+
if values.eql?(:bool)
|
307
|
+
values = BOOLEAN_VALUES
|
308
|
+
set_option(option_symbol, Manager.enum_to_bool(default), 'default') unless default.nil?
|
309
|
+
end
|
310
|
+
# this option value must be a symbol
|
311
|
+
opt[:values] = values
|
312
|
+
value = get_option(option_symbol)
|
313
|
+
help_values = values.map{|i|i.eql?(value) ? highlight_current(i) : i}.join(', ')
|
314
|
+
if values.eql?(BOOLEAN_VALUES)
|
315
|
+
help_values = BOOLEAN_SIMPLE.map{|i|(i.eql?(:yes) && value) || (i.eql?(:no) && !value) ? highlight_current(i) : i}.join(', ')
|
316
|
+
end
|
317
|
+
on_args[0] = "#{description}: #{help_values}"
|
318
|
+
on_args.push(symbol_to_option(option_symbol, 'ENUM'))
|
319
|
+
on_args.push(values)
|
320
|
+
@parser.on(*on_args){|v|set_option(option_symbol, self.class.get_from_list(v.to_s, description, values), 'cmdline')}
|
321
|
+
when :date
|
322
|
+
on_args.push(symbol_to_option(option_symbol, 'DATE'))
|
323
|
+
@parser.on(*on_args) do |v|
|
324
|
+
time_string = case v
|
325
|
+
when 'now' then Manager.time_to_string(Time.now)
|
326
|
+
when /^-([0-9]+)h/ then Manager.time_to_string(Time.now - (Regexp.last_match(1).to_i * 3600))
|
327
|
+
else v
|
328
|
+
end
|
329
|
+
set_option(option_symbol, time_string, 'cmdline')
|
315
330
|
end
|
331
|
+
when :none
|
332
|
+
raise "internal error: missing block for #{option_symbol}" if block.nil?
|
333
|
+
on_args.push(symbol_to_option(option_symbol, nil))
|
334
|
+
on_args.push("-#{short}") if short.is_a?(String)
|
335
|
+
@parser.on(*on_args, &block)
|
336
|
+
else raise 'internal error'
|
316
337
|
end
|
338
|
+
Log.log.debug{"on_args=#{on_args}"}
|
317
339
|
end
|
318
340
|
|
319
|
-
#
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
341
|
+
# Adds each of the keys of specified hash as an option
|
342
|
+
# @param preset_hash [Hash] hash of options to add
|
343
|
+
def add_option_preset(preset_hash, op: :push)
|
344
|
+
Log.log.debug{"add_option_preset=#{preset_hash}"}
|
345
|
+
raise "internal error: default expects Hash: #{preset_hash.class}" unless preset_hash.is_a?(Hash)
|
346
|
+
# incremental override
|
347
|
+
preset_hash.each{|k, v|@unprocessed_defaults.send(op, [k.to_sym, v])}
|
325
348
|
end
|
326
349
|
|
327
350
|
# check if there were unprocessed values to generate error
|
@@ -337,7 +360,7 @@ module Aspera
|
|
337
360
|
return result
|
338
361
|
end
|
339
362
|
|
340
|
-
# get all original options
|
363
|
+
# get all original options on command line used to generate a config in config file
|
341
364
|
def get_options_table(remove_from_remaining: true)
|
342
365
|
result = {}
|
343
366
|
@initial_cli_options.each do |optionval|
|
@@ -347,7 +370,7 @@ module Aspera
|
|
347
370
|
when /^--([^=]+)=(.*)$/
|
348
371
|
name = Regexp.last_match(1)
|
349
372
|
value = Regexp.last_match(2)
|
350
|
-
name.gsub!(OPTION_SEP_LINE,
|
373
|
+
name.gsub!(OPTION_SEP_LINE, OPTION_SEP_SYMB)
|
351
374
|
value = ExtendedValue.instance.evaluate(value)
|
352
375
|
Log.log.debug{"option #{name}=#{value}"}
|
353
376
|
result[name] = value
|
@@ -429,7 +452,7 @@ module Aspera
|
|
429
452
|
|
430
453
|
# generate command line option from option symbol
|
431
454
|
def symbol_to_option(symbol, opt_val)
|
432
|
-
result = '--' + symbol.to_s.gsub(
|
455
|
+
result = '--' + symbol.to_s.gsub(OPTION_SEP_SYMB, OPTION_SEP_LINE)
|
433
456
|
result = result + '=' + opt_val unless opt_val.nil?
|
434
457
|
return result
|
435
458
|
end
|