aspera-cli 4.14.0 → 4.15.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 +54 -3
- data/CONTRIBUTING.md +7 -7
- data/README.md +1457 -880
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/proxy.pac +1 -1
- data/lib/aspera/aoc.rb +198 -127
- data/lib/aspera/ascmd.rb +24 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -171
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +101 -147
- data/lib/aspera/cli/manager.rb +160 -124
- data/lib/aspera/cli/plugin.rb +70 -59
- data/lib/aspera/cli/plugins/alee.rb +0 -1
- data/lib/aspera/cli/plugins/aoc.rb +239 -273
- data/lib/aspera/cli/plugins/ats.rb +8 -5
- data/lib/aspera/cli/plugins/bss.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +516 -375
- data/lib/aspera/cli/plugins/console.rb +40 -0
- data/lib/aspera/cli/plugins/cos.rb +4 -5
- data/lib/aspera/cli/plugins/faspex.rb +99 -84
- data/lib/aspera/cli/plugins/faspex5.rb +179 -148
- data/lib/aspera/cli/plugins/node.rb +219 -153
- data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
- data/lib/aspera/cli/plugins/preview.rb +46 -32
- data/lib/aspera/cli/plugins/server.rb +57 -17
- data/lib/aspera/cli/plugins/shares.rb +34 -12
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +45 -55
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +3 -1
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/environment.rb +17 -6
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +21 -22
- data/lib/aspera/fasp/agent_direct.rb +88 -102
- data/lib/aspera/fasp/agent_httpgw.rb +196 -192
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -34
- data/lib/aspera/fasp/error_info.rb +2 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +43 -184
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +59 -26
- data/lib/aspera/fasp/parameters.yaml +75 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/transfer_spec.rb +1 -1
- data/lib/aspera/fasp/uri.rb +4 -4
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +57 -16
- data/lib/aspera/node.rb +97 -14
- data/lib/aspera/oauth.rb +36 -18
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +24 -13
- data/lib/aspera/preview/utils.rb +19 -26
- data/lib/aspera/rest.rb +103 -72
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +14 -16
- data/lib/aspera/ssh.rb +4 -1
- data/lib/aspera/sync.rb +128 -122
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +33 -15
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/fasp/listener.rb +0 -13
data/lib/aspera/cli/manager.rb
CHANGED
@@ -1,42 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'aspera/cli/extended_value'
|
4
|
+
require 'aspera/cli/error'
|
3
5
|
require 'aspera/colors'
|
4
|
-
require 'aspera/log'
|
5
6
|
require 'aspera/secret_hider'
|
6
|
-
require 'aspera/
|
7
|
-
require 'optparse'
|
7
|
+
require 'aspera/log'
|
8
8
|
require 'io/console'
|
9
|
+
require 'optparse'
|
9
10
|
|
10
11
|
module Aspera
|
11
12
|
module Cli
|
12
|
-
# raised by cli on error conditions
|
13
|
-
class CliError < StandardError; end
|
14
|
-
|
15
|
-
# raised when an unexpected argument is provided
|
16
|
-
class CliBadArgument < Aspera::Cli::CliError; end
|
17
|
-
|
18
|
-
class CliNoSuchId < Aspera::Cli::CliError
|
19
|
-
def initialize(res_type, res_id)
|
20
|
-
msg = "No such #{res_type} identifier: #{res_id}"
|
21
|
-
super(msg)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
13
|
# option is retrieved from another object using accessor
|
26
14
|
class AttrAccessor
|
27
15
|
# attr_accessor :object
|
28
|
-
# attr_accessor :
|
29
|
-
def initialize(object,
|
16
|
+
# attr_accessor :method_name
|
17
|
+
def initialize(object, method_name, option_name)
|
30
18
|
@object = object
|
31
|
-
@
|
19
|
+
@method = method_name
|
20
|
+
@option_name = option_name
|
21
|
+
@has_writer = @object.respond_to?(writer_method)
|
22
|
+
Log.log.debug{"AttrAccessor: #{@option_name}: #{@object.class}.#{@method}: writer=#{@has_writer}"}
|
23
|
+
raise "internal error: #{object} does not respond to #{method_name}" unless @object.respond_to?(@method)
|
32
24
|
end
|
33
25
|
|
34
26
|
def value
|
35
|
-
return @object.send(@
|
27
|
+
return @object.send(@method) if @has_writer
|
28
|
+
return @object.send(@method, @option_name, :get)
|
36
29
|
end
|
37
30
|
|
38
31
|
def value=(val)
|
39
|
-
|
32
|
+
Log.log.trace1{"AttrAccessor: = #{@method} #{@option_name} :set #{val}, writer=#{@has_writer}"}
|
33
|
+
return @object.send(writer_method, val) if @has_writer
|
34
|
+
return @object.send(@method, @option_name, :set, val)
|
35
|
+
end
|
36
|
+
|
37
|
+
def writer_method
|
38
|
+
return "#{@method}="
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
@@ -53,9 +52,10 @@ module Aspera
|
|
53
52
|
# option name separator on command line
|
54
53
|
OPTION_SEP_LINE = '-'
|
55
54
|
# option name separator in code (symbol)
|
56
|
-
|
55
|
+
OPTION_SEP_SYMBOL = '_'
|
56
|
+
SOURCE_USER = 'cmdline' # cspell:disable-line
|
57
57
|
|
58
|
-
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_SYMBOL, :SOURCE_USER
|
59
59
|
|
60
60
|
class << self
|
61
61
|
def enum_to_bool(enum)
|
@@ -68,13 +68,13 @@ module Aspera
|
|
68
68
|
end
|
69
69
|
|
70
70
|
# find shortened string value in allowed symbol list
|
71
|
-
def get_from_list(
|
71
|
+
def get_from_list(short_value, descr, allowed_values)
|
72
72
|
# we accept shortcuts
|
73
|
-
matching_exact = allowed_values.select{|i| i.to_s.eql?(
|
73
|
+
matching_exact = allowed_values.select{|i| i.to_s.eql?(short_value)}
|
74
74
|
return matching_exact.first if matching_exact.length == 1
|
75
|
-
matching = allowed_values.select{|i| i.to_s.start_with?(
|
76
|
-
raise
|
77
|
-
raise
|
75
|
+
matching = allowed_values.select{|i| i.to_s.start_with?(short_value)}
|
76
|
+
raise Cli::BadArgument, bad_arg_message_multi("unknown value for #{descr}: #{short_value}", allowed_values) if matching.empty?
|
77
|
+
raise Cli::BadArgument, bad_arg_message_multi("ambiguous shortcut for #{descr}: #{short_value}", matching) unless matching.length.eql?(1)
|
78
78
|
return enum_to_bool(matching.first) if allowed_values.eql?(BOOLEAN_VALUES)
|
79
79
|
return matching.first
|
80
80
|
end
|
@@ -85,11 +85,19 @@ module Aspera
|
|
85
85
|
|
86
86
|
# change option name with dash to name with underscore
|
87
87
|
def option_line_to_name(name)
|
88
|
-
return name.gsub(OPTION_SEP_LINE,
|
88
|
+
return name.gsub(OPTION_SEP_LINE, OPTION_SEP_SYMBOL)
|
89
89
|
end
|
90
90
|
|
91
91
|
def option_name_to_line(name)
|
92
|
-
return "--#{name.to_s.gsub(
|
92
|
+
return "--#{name.to_s.gsub(OPTION_SEP_SYMBOL, OPTION_SEP_LINE)}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate_type(what, descr, value, type_list)
|
96
|
+
return nil if type_list.nil?
|
97
|
+
raise 'internal error: types must be a Class Array' unless type_list.is_a?(Array) && type_list.all?(Class)
|
98
|
+
raise Cli::BadArgument,
|
99
|
+
"#{what.to_s.capitalize} #{descr} is a #{value.class} but must be #{type_list.length > 1 ? 'one of ' : ''}#{type_list.map(&:name).join(',')}" unless \
|
100
|
+
type_list.any?{|t|value.is_a?(t)}
|
93
101
|
end
|
94
102
|
end
|
95
103
|
|
@@ -97,7 +105,7 @@ module Aspera
|
|
97
105
|
attr_accessor :ask_missing_mandatory, :ask_missing_optional
|
98
106
|
attr_writer :fail_on_missing_mandatory
|
99
107
|
|
100
|
-
def initialize(program_name
|
108
|
+
def initialize(program_name)
|
101
109
|
# command line values not starting with '-'
|
102
110
|
@unprocessed_cmd_line_arguments = []
|
103
111
|
# command line values starting with '-'
|
@@ -118,7 +126,7 @@ module Aspera
|
|
118
126
|
@parser = OptionParser.new
|
119
127
|
@parser.program_name = program_name
|
120
128
|
# options can also be provided by env vars : --param-name -> ASCLI_PARAM_NAME
|
121
|
-
env_prefix = program_name.upcase +
|
129
|
+
env_prefix = program_name.upcase + OPTION_SEP_SYMBOL
|
122
130
|
ENV.each do |k, v|
|
123
131
|
if k.start_with?(env_prefix)
|
124
132
|
@unprocessed_env.push([k[env_prefix.length..-1].downcase.to_sym, v])
|
@@ -127,71 +135,81 @@ module Aspera
|
|
127
135
|
Log.log.debug{"env=#{@unprocessed_env}".red}
|
128
136
|
@unprocessed_cmd_line_options = []
|
129
137
|
@unprocessed_cmd_line_arguments = []
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
@unprocessed_cmd_line_options.push(value)
|
145
|
-
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_command_line(argv)
|
141
|
+
@parser.separator('')
|
142
|
+
@parser.separator('OPTIONS: global')
|
143
|
+
declare(:interactive, 'Use interactive input of missing params', values: :bool, handler: {o: self, m: :ask_missing_mandatory})
|
144
|
+
declare(:ask_options, 'Ask even optional options', values: :bool, handler: {o: self, m: :ask_missing_optional})
|
145
|
+
parse_options!
|
146
|
+
process_options = true
|
147
|
+
until argv.empty?
|
148
|
+
value = argv.shift
|
149
|
+
if process_options && value.start_with?('-')
|
150
|
+
if value.eql?('--')
|
151
|
+
process_options = false
|
146
152
|
else
|
147
|
-
@
|
153
|
+
@unprocessed_cmd_line_options.push(value)
|
148
154
|
end
|
155
|
+
else
|
156
|
+
@unprocessed_cmd_line_arguments.push(value)
|
149
157
|
end
|
150
158
|
end
|
151
159
|
@initial_cli_options = @unprocessed_cmd_line_options.dup
|
152
|
-
Log.log.debug{"add_cmd_line_options:commands/
|
160
|
+
Log.log.debug{"add_cmd_line_options:commands/arguments=#{@unprocessed_cmd_line_arguments},options=#{@unprocessed_cmd_line_options}".red}
|
153
161
|
end
|
154
162
|
|
163
|
+
# @param descr [String] description for help
|
155
164
|
# @param expected is
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
# @param
|
161
|
-
# @param
|
165
|
+
# - Array of allowed value (single value)
|
166
|
+
# - :multiple for remaining values
|
167
|
+
# - :single for a single unconstrained value
|
168
|
+
# - :integer for a single integer value
|
169
|
+
# @param mandatory [Boolean] if true, raise error if option not set
|
170
|
+
# @param type [Class, Array] accepted value type(s)
|
171
|
+
# @param aliases [Hash] map of aliases: key = alias, value = real value
|
172
|
+
# @param default [Object] default value
|
162
173
|
# @return value, list or nil
|
163
174
|
def get_next_argument(descr, expected: :single, mandatory: true, type: nil, aliases: nil, default: nil)
|
164
175
|
unless type.nil?
|
165
|
-
|
176
|
+
type = [type] unless type.is_a?(Array)
|
177
|
+
raise "INTERNAL ERROR: type must be Array of Class: #{type}" unless type.all?(Class)
|
166
178
|
descr = "#{descr} (#{type})"
|
167
179
|
end
|
168
|
-
result =
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
180
|
+
result =
|
181
|
+
if !@unprocessed_cmd_line_arguments.empty?
|
182
|
+
# there are values
|
183
|
+
case expected
|
184
|
+
when :single
|
185
|
+
ExtendedValue.instance.evaluate(@unprocessed_cmd_line_arguments.shift)
|
186
|
+
when :multiple
|
187
|
+
value = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length).map{|v|ExtendedValue.instance.evaluate(v)}
|
188
|
+
# if expecting list and only one arg of type array : it is the list
|
189
|
+
if value.length.eql?(1) && value.first.is_a?(Array)
|
190
|
+
value = value.first
|
191
|
+
end
|
192
|
+
value
|
193
|
+
when Array
|
194
|
+
allowed_values = [].concat(expected)
|
195
|
+
allowed_values.concat(aliases.keys) unless aliases.nil?
|
196
|
+
raise "internal error: only symbols allowed: #{allowed_values}" unless allowed_values.all?(Symbol)
|
197
|
+
self.class.get_from_list(@unprocessed_cmd_line_arguments.shift, descr, allowed_values)
|
198
|
+
else
|
199
|
+
raise 'Internal error: expected: must be single, multiple, or value array'
|
179
200
|
end
|
180
|
-
|
181
|
-
|
182
|
-
|
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)
|
185
|
-
else
|
186
|
-
raise 'internal error'
|
201
|
+
elsif !default.nil? then default
|
202
|
+
# no value provided, either get value interactively, or exception
|
203
|
+
elsif mandatory then get_interactive(:argument, descr, expected: expected)
|
187
204
|
end
|
188
|
-
|
189
|
-
|
190
|
-
result =
|
205
|
+
if result.is_a?(String) && type.eql?([Integer])
|
206
|
+
str_result = result
|
207
|
+
result = Integer(str_result, exception: false)
|
208
|
+
raise Cli::BadArgument, "Invalid integer: #{str_result}" if result.nil?
|
191
209
|
end
|
192
210
|
Log.log.debug{"#{descr}=#{result}"}
|
193
211
|
result = aliases[result] if !aliases.nil? && aliases.key?(result)
|
194
|
-
|
212
|
+
self.class.validate_type(:argument, descr, result, type) unless result.nil? && !mandatory
|
195
213
|
return result
|
196
214
|
end
|
197
215
|
|
@@ -201,52 +219,50 @@ module Aspera
|
|
201
219
|
# either return value or calls handler, can return nil
|
202
220
|
# ask interactively if requested/required
|
203
221
|
# @param mandatory [Boolean] if true, raise error if option not set
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
raise 'Internal Error: allowed_types must be an Array of Class or a Class' unless allowed_types.nil? || allowed_types.is_a?(Array)
|
222
|
+
def get_option(option_symbol, mandatory: false, default: nil)
|
223
|
+
attributes = @declared_options[option_symbol]
|
224
|
+
raise "INTERNAL ERROR: option not declared: #{option_symbol}" unless attributes
|
208
225
|
result = nil
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
raise 'unknown type'
|
217
|
-
end
|
218
|
-
Log.log.debug{"(#{@declared_options[option_symbol][:read_write]}) get #{option_symbol}=#{result}"}
|
226
|
+
case attributes[:read_write]
|
227
|
+
when :accessor
|
228
|
+
result = attributes[:accessor].value
|
229
|
+
when :value
|
230
|
+
result = attributes[:value]
|
231
|
+
else
|
232
|
+
raise 'unknown type'
|
219
233
|
end
|
234
|
+
Log.log.debug{"(#{attributes[:read_write]}) get #{option_symbol}=#{result}"}
|
235
|
+
result = default if result.nil?
|
220
236
|
# do not fail for manual generation if option mandatory but not set
|
221
237
|
result = '' if result.nil? && mandatory && !@fail_on_missing_mandatory
|
222
238
|
# Log.log.debug{"interactive=#{@ask_missing_mandatory}"}
|
223
239
|
if result.nil?
|
224
240
|
if !@ask_missing_mandatory
|
225
|
-
raise
|
241
|
+
raise Cli::BadArgument, "Missing mandatory option: #{option_symbol}" if mandatory
|
226
242
|
elsif @ask_missing_optional || mandatory
|
227
243
|
# ask_missing_mandatory
|
228
244
|
expected = :single
|
229
245
|
# print "please enter: #{option_symbol.to_s}"
|
230
|
-
if @declared_options.key?(option_symbol) &&
|
231
|
-
expected =
|
246
|
+
if @declared_options.key?(option_symbol) && attributes.key?(:values)
|
247
|
+
expected = attributes[:values]
|
232
248
|
end
|
233
249
|
result = get_interactive(:option, option_symbol.to_s, expected: expected)
|
234
250
|
set_option(option_symbol, result, 'interactive')
|
235
251
|
end
|
236
252
|
end
|
237
|
-
|
238
|
-
!mandatory || allowed_types.nil? || allowed_types.any? { |t|result.is_a?(t)}
|
253
|
+
self.class.validate_type(:option, option_symbol, result, attributes[:types]) unless result.nil? && !mandatory
|
239
254
|
return result
|
240
255
|
end
|
241
256
|
|
242
257
|
# set an option value by name, either store value or call handler
|
243
258
|
def set_option(option_symbol, value, where='code override')
|
244
|
-
raise
|
259
|
+
raise Cli::BadArgument, "Unknown option: #{option_symbol}" unless @declared_options.key?(option_symbol)
|
245
260
|
attributes = @declared_options[option_symbol]
|
246
261
|
Log.log.warn("#{option_symbol}: Option is deprecated: #{attributes[:deprecation]}") if attributes[:deprecation]
|
247
262
|
value = ExtendedValue.instance.evaluate(value)
|
248
263
|
value = Manager.enum_to_bool(value) if attributes[:values].eql?(BOOLEAN_VALUES)
|
249
264
|
Log.log.debug{"(#{attributes[:read_write]}/#{where}) set #{option_symbol}=#{value}"}
|
265
|
+
self.class.validate_type(:option, option_symbol, value, attributes[:types])
|
250
266
|
case attributes[:read_write]
|
251
267
|
when :accessor
|
252
268
|
attributes[:accessor].value = value
|
@@ -269,9 +285,11 @@ module Aspera
|
|
269
285
|
# @param block [Proc] block to execute when option is found
|
270
286
|
def declare(option_symbol, description, handler: nil, default: nil, values: nil, short: nil, coerce: nil, types: nil, deprecation: nil, &block)
|
271
287
|
raise "INTERNAL ERROR: #{option_symbol} already declared" if @declared_options.key?(option_symbol)
|
288
|
+
# raise "INTERNAL ERROR: #{option_symbol} clash with another option" if
|
289
|
+
# @declared_options.keys.map(&:to_s).any?{|k|k.start_with?(option_symbol.to_s) || option_symbol.to_s.start_with?(k)}
|
272
290
|
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
|
291
|
+
raise "INTERNAL ERROR: #{option_symbol} description does not start with capital" unless description[0] == description[0].upcase
|
292
|
+
raise "INTERNAL ERROR: #{option_symbol} shall use :types" if ['hash', 'extended value'].any?{|s|description.downcase.include?(s) }
|
275
293
|
opt = @declared_options[option_symbol] = {
|
276
294
|
read_write: handler.nil? ? :value : :accessor,
|
277
295
|
# by default passwords and secrets are sensitive, else specify when declaring the option
|
@@ -279,7 +297,7 @@ module Aspera
|
|
279
297
|
}
|
280
298
|
if !types.nil?
|
281
299
|
types = [types] unless types.is_a?(Array)
|
282
|
-
raise "INTERNAL ERROR: types must be
|
300
|
+
raise "INTERNAL ERROR: types must be Array of Class: #{types}" unless types.all?(Class)
|
283
301
|
opt[:types] = types
|
284
302
|
description = "#{description} (#{types.map(&:name).join(', ')})"
|
285
303
|
end
|
@@ -292,7 +310,7 @@ module Aspera
|
|
292
310
|
raise 'internal error' unless handler.is_a?(Hash)
|
293
311
|
raise 'internal error' unless handler.keys.sort.eql?(%i[m o])
|
294
312
|
Log.log.debug{"set attr obj #{option_symbol} (#{handler[:o]},#{handler[:m]})"}
|
295
|
-
opt[:accessor] = AttrAccessor.new(handler[:o], handler[:m])
|
313
|
+
opt[:accessor] = AttrAccessor.new(handler[:o], handler[:m], option_symbol)
|
296
314
|
end
|
297
315
|
set_option(option_symbol, default, 'default') unless default.nil?
|
298
316
|
on_args = [description]
|
@@ -301,7 +319,7 @@ module Aspera
|
|
301
319
|
on_args.push(symbol_to_option(option_symbol, 'VALUE'))
|
302
320
|
on_args.push("-#{short}VALUE") unless short.nil?
|
303
321
|
on_args.push(coerce) unless coerce.nil?
|
304
|
-
@parser.on(*on_args) { |v| set_option(option_symbol, v,
|
322
|
+
@parser.on(*on_args) { |v| set_option(option_symbol, v, SOURCE_USER) }
|
305
323
|
when Array, :bool
|
306
324
|
if values.eql?(:bool)
|
307
325
|
values = BOOLEAN_VALUES
|
@@ -317,7 +335,7 @@ module Aspera
|
|
317
335
|
on_args[0] = "#{description}: #{help_values}"
|
318
336
|
on_args.push(symbol_to_option(option_symbol, 'ENUM'))
|
319
337
|
on_args.push(values)
|
320
|
-
@parser.on(*on_args){|v|set_option(option_symbol, self.class.get_from_list(v.to_s, description, values),
|
338
|
+
@parser.on(*on_args){|v|set_option(option_symbol, self.class.get_from_list(v.to_s, description, values), SOURCE_USER)}
|
321
339
|
when :date
|
322
340
|
on_args.push(symbol_to_option(option_symbol, 'DATE'))
|
323
341
|
@parser.on(*on_args) do |v|
|
@@ -326,14 +344,14 @@ module Aspera
|
|
326
344
|
when /^-([0-9]+)h/ then Manager.time_to_string(Time.now - (Regexp.last_match(1).to_i * 3600))
|
327
345
|
else v
|
328
346
|
end
|
329
|
-
set_option(option_symbol, time_string,
|
347
|
+
set_option(option_symbol, time_string, SOURCE_USER)
|
330
348
|
end
|
331
349
|
when :none
|
332
350
|
raise "internal error: missing block for #{option_symbol}" if block.nil?
|
333
351
|
on_args.push(symbol_to_option(option_symbol, nil))
|
334
352
|
on_args.push("-#{short}") if short.is_a?(String)
|
335
353
|
@parser.on(*on_args, &block)
|
336
|
-
else raise
|
354
|
+
else raise "internal error: Unknown type for values: #{values} / #{values.class}"
|
337
355
|
end
|
338
356
|
Log.log.debug{"on_args=#{on_args}"}
|
339
357
|
end
|
@@ -363,31 +381,32 @@ module Aspera
|
|
363
381
|
# get all original options on command line used to generate a config in config file
|
364
382
|
def get_options_table(remove_from_remaining: true)
|
365
383
|
result = {}
|
366
|
-
@initial_cli_options.each do |
|
367
|
-
case
|
384
|
+
@initial_cli_options.each do |option_value|
|
385
|
+
case option_value
|
368
386
|
when /^--([^=]+)$/
|
369
387
|
# ignore
|
370
388
|
when /^--([^=]+)=(.*)$/
|
371
389
|
name = Regexp.last_match(1)
|
372
390
|
value = Regexp.last_match(2)
|
373
|
-
name.gsub!(OPTION_SEP_LINE,
|
391
|
+
name.gsub!(OPTION_SEP_LINE, OPTION_SEP_SYMBOL)
|
374
392
|
value = ExtendedValue.instance.evaluate(value)
|
375
393
|
Log.log.debug{"option #{name}=#{value}"}
|
376
394
|
result[name] = value
|
377
|
-
@unprocessed_cmd_line_options.delete(
|
395
|
+
@unprocessed_cmd_line_options.delete(option_value) if remove_from_remaining
|
378
396
|
else
|
379
|
-
raise
|
397
|
+
raise Cli::BadArgument, "wrong option format: #{option_value}"
|
380
398
|
end
|
381
399
|
end
|
382
400
|
return result
|
383
401
|
end
|
384
402
|
|
385
|
-
#
|
386
|
-
|
403
|
+
# @param only_defined [Boolean] if true, only return options that were defined
|
404
|
+
# @return [Hash] options as taken from config file and command line just before command execution
|
405
|
+
def known_options(only_defined: false)
|
387
406
|
result = {}
|
388
|
-
@declared_options.each_key do |
|
389
|
-
v = get_option(
|
390
|
-
result[
|
407
|
+
@declared_options.each_key do |option_symbol|
|
408
|
+
v = get_option(option_symbol)
|
409
|
+
result[option_symbol] = v unless only_defined && v.nil?
|
391
410
|
end
|
392
411
|
return result
|
393
412
|
end
|
@@ -416,21 +435,36 @@ module Aspera
|
|
416
435
|
@unprocessed_cmd_line_options = unknown_options
|
417
436
|
end
|
418
437
|
|
419
|
-
private
|
420
|
-
|
421
438
|
def prompt_user_input(prompt, sensitive)
|
422
439
|
return $stdin.getpass("#{prompt}> ") if sensitive
|
423
440
|
print("#{prompt}> ")
|
424
|
-
|
441
|
+
line = $stdin.gets
|
442
|
+
raise 'Unexpected end of standard input' if line.nil?
|
443
|
+
return line.chomp
|
444
|
+
end
|
445
|
+
|
446
|
+
# prompt user for input in a list of symbols
|
447
|
+
# @param prompt [String] prompt to display
|
448
|
+
# @param sym_list [Array] list of symbols to select from
|
449
|
+
# @return [Symbol] selected symbol
|
450
|
+
def prompt_user_input_in_list(prompt, sym_list)
|
451
|
+
loop do
|
452
|
+
input = prompt_user_input(prompt, false).to_sym
|
453
|
+
if sym_list.any?{|a|a.eql?(input)}
|
454
|
+
return input
|
455
|
+
else
|
456
|
+
$stderr.puts("No such #{prompt}: #{input}, select one of: #{sym_list.join(', ')}")
|
457
|
+
end
|
458
|
+
end
|
425
459
|
end
|
426
460
|
|
427
461
|
def get_interactive(type, descr, expected: :single)
|
428
462
|
if !@ask_missing_mandatory
|
429
|
-
raise
|
430
|
-
raise
|
463
|
+
raise Cli::BadArgument, self.class.bad_arg_message_multi("missing: #{descr}", expected) if expected.is_a?(Array)
|
464
|
+
raise Cli::BadArgument, "missing argument (#{expected}): #{descr}"
|
431
465
|
end
|
432
466
|
result = nil
|
433
|
-
sensitive = type.eql?(:option) && @declared_options[descr.to_sym][:sensitive]
|
467
|
+
sensitive = type.eql?(:option) && @declared_options[descr.to_sym].is_a?(Hash) && @declared_options[descr.to_sym][:sensitive]
|
434
468
|
default_prompt = "#{type}: #{descr}"
|
435
469
|
# ask interactively
|
436
470
|
case expected
|
@@ -450,9 +484,11 @@ module Aspera
|
|
450
484
|
return result
|
451
485
|
end
|
452
486
|
|
487
|
+
private
|
488
|
+
|
453
489
|
# generate command line option from option symbol
|
454
490
|
def symbol_to_option(symbol, opt_val)
|
455
|
-
result = '--' + symbol.to_s.gsub(
|
491
|
+
result = '--' + symbol.to_s.gsub(OPTION_SEP_SYMBOL, OPTION_SEP_LINE)
|
456
492
|
result = result + '=' + opt_val unless opt_val.nil?
|
457
493
|
return result
|
458
494
|
end
|