aspera-cli 4.27.0 → 4.27.1
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 +29 -0
- data/CONTRIBUTING.md +1 -7
- data/TODO.md +50 -0
- data/docs/README.md +708 -488
- data/lib/aspera/agent/base.rb +10 -2
- data/lib/aspera/agent/connect.rb +7 -7
- data/lib/aspera/agent/desktop.rb +3 -3
- data/lib/aspera/agent/direct.rb +48 -19
- data/lib/aspera/agent/factory.rb +3 -3
- data/lib/aspera/agent/httpgw.rb +55 -14
- data/lib/aspera/agent/node.rb +2 -2
- data/lib/aspera/agent/transferd.rb +12 -12
- data/lib/aspera/api/aoc.rb +28 -28
- data/lib/aspera/api/cos_node.rb +10 -9
- data/lib/aspera/api/faspex.rb +2 -2
- data/lib/aspera/api/httpgw.rb +20 -20
- data/lib/aspera/api/node.rb +31 -53
- data/lib/aspera/ascmd.rb +13 -13
- data/lib/aspera/ascp/installation.rb +43 -36
- data/lib/aspera/ascp/management.rb +6 -6
- data/lib/aspera/assert.rb +12 -13
- data/lib/aspera/cli/ascp_actions.rb +7 -7
- data/lib/aspera/cli/async_transfer_store.rb +49 -5
- data/lib/aspera/cli/bootstrapper.rb +9 -9
- data/lib/aspera/cli/command_registry.rb +1 -1
- data/lib/aspera/cli/command_spec.rb +6 -3
- data/lib/aspera/cli/context.rb +3 -3
- data/lib/aspera/cli/extended_value.rb +26 -26
- data/lib/aspera/cli/formatter.rb +76 -54
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +22 -29
- data/lib/aspera/cli/mailer.rb +7 -7
- data/lib/aspera/cli/mcp_tool.rb +31 -4
- data/lib/aspera/cli/option_declarator.rb +77 -0
- data/lib/aspera/cli/options.schema.yaml +273 -3
- data/lib/aspera/cli/parser.rb +539 -280
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +297 -313
- data/lib/aspera/cli/plugins/ats.rb +25 -21
- data/lib/aspera/cli/plugins/base.rb +357 -186
- data/lib/aspera/cli/plugins/basic_auth.rb +0 -12
- data/lib/aspera/cli/plugins/config.rb +76 -73
- data/lib/aspera/cli/plugins/console.rb +8 -8
- data/lib/aspera/cli/plugins/cos.rb +4 -3
- data/lib/aspera/cli/plugins/factory.rb +2 -2
- data/lib/aspera/cli/plugins/faspex5.rb +259 -248
- data/lib/aspera/cli/plugins/faspio.rb +7 -2
- data/lib/aspera/cli/plugins/httpgw.rb +1 -1
- data/lib/aspera/cli/plugins/mcp.rb +11 -11
- data/lib/aspera/cli/plugins/node.rb +227 -283
- data/lib/aspera/cli/plugins/orchestrator.rb +23 -23
- data/lib/aspera/cli/plugins/preview.rb +61 -64
- data/lib/aspera/cli/plugins/server.rb +36 -31
- data/lib/aspera/cli/plugins/shares.rb +174 -135
- data/lib/aspera/cli/preset_actions.rb +39 -19
- data/lib/aspera/cli/preset_manager.rb +74 -32
- data/lib/aspera/cli/result.rb +18 -16
- data/lib/aspera/cli/runner.rb +75 -39
- data/lib/aspera/cli/secret_finder.rb +1 -1
- data/lib/aspera/cli/sync_actions.rb +36 -35
- data/lib/aspera/cli/terminal_formatter.rb +2 -2
- data/lib/aspera/cli/transfer_actions.rb +17 -10
- data/lib/aspera/cli/transfer_agent.rb +44 -44
- data/lib/aspera/cli/transfer_progress.rb +7 -7
- data/lib/aspera/cli/vault_manager.rb +34 -12
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +17 -20
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +16 -16
- data/lib/aspera/command_line_converter.rb +1 -1
- data/lib/aspera/coverage.rb +1 -2
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/dot_container.rb +8 -8
- data/lib/aspera/environment.rb +12 -12
- data/lib/aspera/faspex_gw.rb +4 -4
- data/lib/aspera/faspex_postproc.rb +14 -9
- data/lib/aspera/graphql.rb +4 -2
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc/client.rb +1 -1
- data/lib/aspera/keychain/base.rb +10 -3
- data/lib/aspera/keychain/encrypted_hash.rb +92 -17
- data/lib/aspera/keychain/factory.rb +17 -5
- data/lib/aspera/keychain/hashicorp_vault.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +23 -25
- data/lib/aspera/keychain/one_password_api.rb +86 -0
- data/lib/aspera/keychain/one_password_base.rb +34 -0
- data/lib/aspera/keychain/one_password_cli.rb +98 -0
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +13 -13
- data/lib/aspera/markdown.rb +5 -5
- data/lib/aspera/nagios.rb +8 -8
- data/lib/aspera/node_simulator.rb +33 -27
- data/lib/aspera/oauth/base.rb +4 -5
- data/lib/aspera/oauth/boot.rb +3 -3
- data/lib/aspera/oauth/factory.rb +5 -5
- data/lib/aspera/oauth/jwt.rb +5 -7
- data/lib/aspera/oauth/web.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +8 -8
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +49 -46
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +5 -5
- data/lib/aspera/preview/utils.rb +65 -18
- data/lib/aspera/products/connect.rb +2 -2
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +2 -2
- data/lib/aspera/products/transferd.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +10 -10
- data/lib/aspera/rest.rb +149 -115
- data/lib/aspera/rest_error_analyzer.rb +3 -3
- data/lib/aspera/rest_list.rb +10 -9
- data/lib/aspera/schema/IBM Aspera Node API-4.4.6.yaml +6232 -0
- data/lib/aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml +249 -0
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +4653 -0
- data/lib/aspera/schema/documentation.rb +17 -11
- data/lib/aspera/schema/reader.rb +78 -4
- data/lib/aspera/schema/registry.rb +26 -7
- data/lib/aspera/secret_hider.rb +5 -5
- data/lib/aspera/ssh.rb +64 -31
- data/lib/aspera/ssl.rb +3 -3
- data/lib/aspera/sync/database.rb +2 -2
- data/lib/aspera/sync/operations.rb +13 -13
- data/lib/aspera/temp_file_manager.rb +24 -4
- data/lib/aspera/transfer/faux_file.rb +2 -2
- data/lib/aspera/transfer/parameters.rb +15 -15
- data/lib/aspera/transfer/resumer.rb +9 -9
- data/lib/aspera/transfer/spec.rb +1 -1
- data/lib/aspera/uri_reader.rb +52 -15
- data/lib/aspera/web_auth.rb +6 -5
- data/lib/aspera/web_server_simple.rb +9 -9
- data/lib/aspera/yaml.rb +4 -4
- data.tar.gz.sig +0 -0
- metadata +10 -4
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/plugins/faspex.rb +0 -576
- data/lib/aspera/transfer/uri.rb +0 -56
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# cspell:ignore jsonpp
|
|
4
4
|
require 'aspera/schema/registry'
|
|
5
|
+
require 'aspera/cli/option_declarator'
|
|
5
6
|
require 'aspera/cli/special_values'
|
|
6
7
|
require 'aspera/cli/terminal_formatter'
|
|
7
8
|
require 'aspera/preview/terminal'
|
|
@@ -22,6 +23,33 @@ module Aspera
|
|
|
22
23
|
module Cli
|
|
23
24
|
# Take care of CLI output on terminal
|
|
24
25
|
class Formatter
|
|
26
|
+
extend OptionDeclarator
|
|
27
|
+
|
|
28
|
+
# remove a fields from the list
|
|
29
|
+
FIELDS_LESS = '-'
|
|
30
|
+
# supported output formats
|
|
31
|
+
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
|
32
|
+
# user output levels
|
|
33
|
+
DISPLAY_LEVELS = %i[info data error].freeze
|
|
34
|
+
# column names for single object display in table
|
|
35
|
+
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
36
|
+
# Terminal: vertical separator for list of strings.
|
|
37
|
+
STR_LST_SEP_VERT = "\n"
|
|
38
|
+
|
|
39
|
+
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES, :STR_LST_SEP_VERT
|
|
40
|
+
|
|
41
|
+
option :out, description: 'Output rendering options (dot-notation: format, level, file, fields, select, table[.pivot], flat, secrets, img)', schema: Schema::Registry::OUT_OPTIONS
|
|
42
|
+
option :display, description: 'Output only some information', allowed: DISPLAY_LEVELS, default: :data, deprecation: 'use --out.level'
|
|
43
|
+
option :format, description: 'Output format (also: --out.format)', allowed: DISPLAY_FORMATS, default: :table
|
|
44
|
+
option :output, description: 'Destination for results', deprecation: 'use --out.file'
|
|
45
|
+
option :fields, description: "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF} (also: --out.fields)", allowed: [String, Array, Regexp, Proc], default: SpecialValues::DEF
|
|
46
|
+
option :select, description: 'Select only some items in lists: column, value (also: --out.select)', allowed: [Hash, Proc]
|
|
47
|
+
option :table_style, description: '(Table) Display style', allowed: [Hash], deprecation: 'use --out.table'
|
|
48
|
+
option :flat_hash, description: '(Table) Display deep values as additional keys', allowed: Type::BOOLEAN, default: true, deprecation: 'use --out.flat'
|
|
49
|
+
option :multi_single, description: '(Table) Control how object list is displayed as single table, or multiple objects', allowed: %i[no yes single], default: :no, deprecation: 'use --out.table.pivot'
|
|
50
|
+
option :show_secrets, description: 'Show secrets on command output', allowed: Type::BOOLEAN, default: false, deprecation: 'use --out.secrets'
|
|
51
|
+
option :image, schema: Schema::Registry::IMAGE_OPTIONS, deprecation: 'use --out.img'
|
|
52
|
+
|
|
25
53
|
class << self
|
|
26
54
|
# Replace special values with a readable version on terminal
|
|
27
55
|
def replace_specific_for_terminal(input_hash, string_list_separator)
|
|
@@ -58,7 +86,7 @@ module Aspera
|
|
|
58
86
|
end
|
|
59
87
|
|
|
60
88
|
def all_but(list)
|
|
61
|
-
Array(list).map{ |i| "#{FIELDS_LESS}#{i}"}.unshift(SpecialValues::ALL)
|
|
89
|
+
Array(list).map { |i| "#{FIELDS_LESS}#{i}" }.unshift(SpecialValues::ALL)
|
|
62
90
|
end
|
|
63
91
|
end
|
|
64
92
|
|
|
@@ -109,38 +137,20 @@ module Aspera
|
|
|
109
137
|
end
|
|
110
138
|
end
|
|
111
139
|
|
|
112
|
-
class << self
|
|
113
|
-
# Declare all formatter CLI options (metadata only - no handler binding yet).
|
|
114
|
-
# @param options [Aspera::Cli::Parser]
|
|
115
|
-
# @return [nil]
|
|
116
|
-
def declare_options(options)
|
|
117
|
-
options.declare(:display, description: 'Output only some information', allowed: DISPLAY_LEVELS, default: :data)
|
|
118
|
-
options.declare(:format, description: 'Output format', allowed: DISPLAY_FORMATS, default: :table)
|
|
119
|
-
options.declare(:output, description: 'Destination for results')
|
|
120
|
-
options.declare(:fields, description: "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", allowed: [String, Array, Regexp, Proc], default: SpecialValues::DEF)
|
|
121
|
-
options.declare(:select, description: 'Select only some items in lists: column, value', allowed: [Hash, Proc])
|
|
122
|
-
options.declare(:table_style, description: '(Table) Display style', allowed: [Hash])
|
|
123
|
-
options.declare(:flat_hash, description: '(Table) Display deep values as additional keys', allowed: Allowed::TYPES_BOOLEAN, default: true)
|
|
124
|
-
options.declare(:multi_single, description: '(Table) Control how object list is displayed as single table, or multiple objects', allowed: %i[no yes single], default: :no)
|
|
125
|
-
options.declare(:show_secrets, description: 'Show secrets on command output', allowed: Allowed::TYPES_BOOLEAN, default: false)
|
|
126
|
-
options.declare(:image, schema: Schema::Registry::IMAGE_OPTIONS)
|
|
127
|
-
nil
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
140
|
# Bind all formatter options to this instance using set_handler.
|
|
132
141
|
# Called from Runner after Formatter.new.
|
|
133
142
|
# @param options [Aspera::Cli::Parser]
|
|
134
143
|
# @return [nil]
|
|
135
144
|
def bind_options(options)
|
|
136
|
-
|
|
145
|
+
@parser = options
|
|
146
|
+
%i[out display format output fields select table_style flat_hash multi_single show_secrets image].each do |opt|
|
|
137
147
|
options.set_handler(opt, object: self, method: :option_handler)
|
|
138
148
|
end
|
|
139
149
|
nil
|
|
140
150
|
end
|
|
141
151
|
|
|
142
152
|
# Getter/setter handler called by the option manager for all formatter options
|
|
143
|
-
# @param option_symbol [Symbol] Option name (one of :format, :output, :display, :fields, :select, :table_style, :flat_hash, :multi_single, :show_secrets, :image)
|
|
153
|
+
# @param option_symbol [Symbol] Option name (one of :out, :format, :output, :display, :fields, :select, :table_style, :flat_hash, :multi_single, :show_secrets, :image)
|
|
144
154
|
# @param operation [Symbol] :get or :set
|
|
145
155
|
# @param value [Object, nil] Value to set (only used when operation is :set)
|
|
146
156
|
# @return [Object, nil] Current option value when operation is :get; nil otherwise
|
|
@@ -148,6 +158,29 @@ module Aspera
|
|
|
148
158
|
Aspera.assert_values(operation, %i[set get])
|
|
149
159
|
case operation
|
|
150
160
|
when :set
|
|
161
|
+
# --out is a composite hash: dispatch each sub-key through the parser (validation included)
|
|
162
|
+
if option_symbol.eql?(:out)
|
|
163
|
+
Aspera.assert_type(value, Hash)
|
|
164
|
+
value.each do |k, v|
|
|
165
|
+
case k.to_sym
|
|
166
|
+
when :format then @parser.set_option(:format, v, warn_deprecation: false)
|
|
167
|
+
when :level then @parser.set_option(:display, v, warn_deprecation: false)
|
|
168
|
+
when :file then @parser.set_option(:output, v, warn_deprecation: false)
|
|
169
|
+
when :fields then @parser.set_option(:fields, v, warn_deprecation: false)
|
|
170
|
+
when :select then @parser.set_option(:select, v, warn_deprecation: false)
|
|
171
|
+
when :table
|
|
172
|
+
Aspera.assert_type(v, Hash)
|
|
173
|
+
table_hash = v.transform_keys(&:to_sym)
|
|
174
|
+
@parser.set_option(:multi_single, table_hash.delete(:pivot).to_s, warn_deprecation: false) if table_hash.key?(:pivot)
|
|
175
|
+
@parser.set_option(:table_style, table_hash, warn_deprecation: false) unless table_hash.empty?
|
|
176
|
+
when :flat then @parser.set_option(:flat_hash, v, warn_deprecation: false)
|
|
177
|
+
when :secrets then @parser.set_option(:show_secrets, v, warn_deprecation: false)
|
|
178
|
+
when :img then @parser.set_option(:image, v, warn_deprecation: false)
|
|
179
|
+
else Aspera.error_unexpected_value(k) { 'out sub-option (format, level, file, fields, select, table[.pivot], flat, secrets, img)' }
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
return
|
|
183
|
+
end
|
|
151
184
|
@options[option_symbol] = value
|
|
152
185
|
# special handling of some options
|
|
153
186
|
case option_symbol
|
|
@@ -161,12 +194,14 @@ module Aspera
|
|
|
161
194
|
end
|
|
162
195
|
when :image
|
|
163
196
|
# get list if key arguments of method
|
|
164
|
-
allowed_options = Preview::Terminal.method(:build).parameters.select{ |i| i[0].eql?(:key)}.map{ |i| i[1]}
|
|
197
|
+
allowed_options = Preview::Terminal.method(:build).parameters.select { |i| i[0].eql?(:key) }.map { |i| i[1] }
|
|
165
198
|
# check that only supported options are given
|
|
166
199
|
unknown_options = value.keys.map(&:to_sym) - allowed_options
|
|
167
|
-
Aspera.assert(unknown_options.empty?){"Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}"}
|
|
200
|
+
Aspera.assert(unknown_options.empty?) { "Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" }
|
|
168
201
|
end
|
|
169
|
-
when :get
|
|
202
|
+
when :get
|
|
203
|
+
return if option_symbol.eql?(:out)
|
|
204
|
+
return @options[option_symbol]
|
|
170
205
|
else Aspera.error_unreachable_line
|
|
171
206
|
end
|
|
172
207
|
nil
|
|
@@ -212,9 +247,9 @@ module Aspera
|
|
|
212
247
|
# @param result [Result] Result object to display
|
|
213
248
|
def display_results(result)
|
|
214
249
|
require 'aspera/cli/result'
|
|
215
|
-
Aspera.assert_type(result, Cli::Result){'result must be a Result object'}
|
|
250
|
+
Aspera.assert_type(result, Cli::Result) { 'result must be a Result object' }
|
|
216
251
|
|
|
217
|
-
Log.log.debug{"display_results: result class=#{result.class.name}"}
|
|
252
|
+
Log.log.debug { "display_results: result class=#{result.class.name}" }
|
|
218
253
|
Log.dump(:data, result.data, level: :trace1)
|
|
219
254
|
Log.dump(:fields, result.fields, level: :trace1)
|
|
220
255
|
|
|
@@ -258,14 +293,14 @@ module Aspera
|
|
|
258
293
|
|
|
259
294
|
# @return [Array<String>] all fields of all objects in list of objects
|
|
260
295
|
def all_fields(data)
|
|
261
|
-
data.each_with_object({}){ |v, m| v.each_key{ |c| m[c] = true}}.keys
|
|
296
|
+
data.each_with_object({}) { |v, m| v.each_key { |c| m[c] = true } }.keys
|
|
262
297
|
end
|
|
263
298
|
|
|
264
299
|
# @return [Array<String>] the list of fields to display
|
|
265
300
|
# @param data [Array<Hash>] data to display
|
|
266
301
|
# @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
|
|
267
302
|
def compute_fields(data, default)
|
|
268
|
-
Log.log.debug{"compute_fields: data:#{data.class} default:#{default.class} #{default}"}
|
|
303
|
+
Log.log.debug { "compute_fields: data:#{data.class} default:#{default.class} #{default}" }
|
|
269
304
|
Log.dump(:compute_fields_default, default, level: :trace1)
|
|
270
305
|
# the requested list of fields, but if can contain special values
|
|
271
306
|
request =
|
|
@@ -273,8 +308,8 @@ module Aspera
|
|
|
273
308
|
# when NilClass then [SpecialValues::DEF]
|
|
274
309
|
when String then @options[:fields].split(',')
|
|
275
310
|
when Array then @options[:fields]
|
|
276
|
-
when Regexp then return all_fields(data).select{ |i| i.match(@options[:fields])}
|
|
277
|
-
when Proc then return all_fields(data).select{ |i| @options[:fields].call(i)}
|
|
311
|
+
when Regexp then return all_fields(data).select { |i| i.match(@options[:fields]) }
|
|
312
|
+
when Proc then return all_fields(data).select { |i| @options[:fields].call(i) }
|
|
278
313
|
else Aspera.error_unexpected_value(@options[:fields])
|
|
279
314
|
end
|
|
280
315
|
Aspera.assert_array_all(request, String)
|
|
@@ -291,12 +326,12 @@ module Aspera
|
|
|
291
326
|
# get the list of all column names used in all lines, not just first one, as all lines may have different columns
|
|
292
327
|
request.unshift(*all_fields(data))
|
|
293
328
|
when SpecialValues::DEF
|
|
294
|
-
default = all_fields(data).select{ |i| default.call(i)} if default.is_a?(Proc)
|
|
329
|
+
default = all_fields(data).select { |i| default.call(i) } if default.is_a?(Proc)
|
|
295
330
|
default = all_fields(data) if default.nil?
|
|
296
331
|
request.unshift(*default)
|
|
297
332
|
else
|
|
298
333
|
if removal
|
|
299
|
-
result = result.reject{ |i| i.eql?(item)}
|
|
334
|
+
result = result.reject { |i| i.eql?(item) }
|
|
300
335
|
else
|
|
301
336
|
result.push(item)
|
|
302
337
|
end
|
|
@@ -312,12 +347,12 @@ module Aspera
|
|
|
312
347
|
return data unless data.is_a?(Array)
|
|
313
348
|
# by default, keep all data intact
|
|
314
349
|
return data if @options[:fields].eql?(SpecialValues::DEF) && @options[:select].nil?
|
|
315
|
-
Aspera.assert_array_all(data, Hash){'filter or select'}
|
|
350
|
+
Aspera.assert_array_all(data, Hash) { 'filter or select' }
|
|
316
351
|
filter_columns_on_select(data)
|
|
317
352
|
return data if @options[:fields].eql?(SpecialValues::DEF)
|
|
318
353
|
selected_fields = compute_fields(data, @options[:fields])
|
|
319
|
-
return data.map{ |i| i[selected_fields.first]} if selected_fields.length == 1
|
|
320
|
-
return data.map{ |i| i.slice(*selected_fields)}
|
|
354
|
+
return data.map { |i| i[selected_fields.first] } if selected_fields.length == 1
|
|
355
|
+
return data.map { |i| i.slice(*selected_fields) }
|
|
321
356
|
end
|
|
322
357
|
|
|
323
358
|
# filter the list of items on the select option
|
|
@@ -326,12 +361,12 @@ module Aspera
|
|
|
326
361
|
case @options[:select]
|
|
327
362
|
when Proc
|
|
328
363
|
begin
|
|
329
|
-
data.select!{ |i| @options[:select].call(i)}
|
|
364
|
+
data.select! { |i| @options[:select].call(i) }
|
|
330
365
|
rescue StandardError => e
|
|
331
366
|
raise Cli::BadArgument, "Error in user-provided ruby lambda code during select: #{e.message}"
|
|
332
367
|
end
|
|
333
368
|
when Hash
|
|
334
|
-
@options[:select].each{ |k, v| data.select!{ |i| i[k].eql?(v)}}
|
|
369
|
+
@options[:select].each { |k, v| data.select! { |i| i[k].eql?(v) } }
|
|
335
370
|
end
|
|
336
371
|
end
|
|
337
372
|
|
|
@@ -351,7 +386,7 @@ module Aspera
|
|
|
351
386
|
format_style = @options[:table_style].symbolize_keys
|
|
352
387
|
string_list_separator = format_style.delete(:str_lst_sep) || STR_LST_SEP_VERT
|
|
353
388
|
# convert data to string, and keep only display fields
|
|
354
|
-
object_array.each{ |i| self.class.replace_specific_for_terminal(i, string_list_separator)}
|
|
389
|
+
object_array.each { |i| self.class.replace_specific_for_terminal(i, string_list_separator) }
|
|
355
390
|
# if table has only one element, and only one field, display the value
|
|
356
391
|
if object_array.length == 1 && fields.length == 1
|
|
357
392
|
Log.log.debug("single element, field: #{fields.first}")
|
|
@@ -367,9 +402,9 @@ module Aspera
|
|
|
367
402
|
Log.dump(:object_array, object_array)
|
|
368
403
|
Log.dump(:fields, fields)
|
|
369
404
|
# convert data to string, and keep only display fields
|
|
370
|
-
final_table_rows = object_array.map{ |r| fields.map{ |c| r[c].to_s}}
|
|
405
|
+
final_table_rows = object_array.map { |r| fields.map { |c| r[c].to_s } }
|
|
371
406
|
# remove empty rows
|
|
372
|
-
final_table_rows.select!{ |i| !(i.is_a?(Hash) && i.empty?)}
|
|
407
|
+
final_table_rows.select! { |i| !(i.is_a?(Hash) && i.empty?) }
|
|
373
408
|
# fields: list of column names to display
|
|
374
409
|
case @options[:format]
|
|
375
410
|
when :table
|
|
@@ -406,19 +441,6 @@ module Aspera
|
|
|
406
441
|
end
|
|
407
442
|
nil
|
|
408
443
|
end
|
|
409
|
-
|
|
410
|
-
# remove a fields from the list
|
|
411
|
-
FIELDS_LESS = '-'
|
|
412
|
-
# supported output formats
|
|
413
|
-
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
|
414
|
-
# user output levels
|
|
415
|
-
DISPLAY_LEVELS = %i[info data error].freeze
|
|
416
|
-
# column names for single object display in table
|
|
417
|
-
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
418
|
-
# Terminal: vertical separator for list of strings.
|
|
419
|
-
STR_LST_SEP_VERT = "\n"
|
|
420
|
-
|
|
421
|
-
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES, :STR_LST_SEP_VERT
|
|
422
444
|
end
|
|
423
445
|
end
|
|
424
446
|
end
|
|
@@ -51,7 +51,7 @@ module Aspera
|
|
|
51
51
|
)
|
|
52
52
|
current_date = Date.today
|
|
53
53
|
last_check_days = (current_date - Date.strptime(last_check_array.first, GEM_CHECK_DATE_FMT)) rescue nil
|
|
54
|
-
Log.log.debug{"gem check new version: #{delay_days}, #{last_check_days}, #{current_date}, #{last_check_array}"}
|
|
54
|
+
Log.log.debug { "gem check new version: #{delay_days}, #{last_check_days}, #{current_date}, #{last_check_array}" }
|
|
55
55
|
return if !last_check_days.nil? && last_check_days < delay_days
|
|
56
56
|
last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
|
|
57
57
|
check_date_persist.save
|
data/lib/aspera/cli/hints.rb
CHANGED
|
@@ -142,10 +142,10 @@ module Aspera
|
|
|
142
142
|
next unless message.eql?(m)
|
|
143
143
|
when Regexp
|
|
144
144
|
next unless message.match?(m)
|
|
145
|
-
else Aspera.error_unexpected_value(m){'hint match'}
|
|
145
|
+
else Aspera.error_unexpected_value(m) { 'hint match' }
|
|
146
146
|
end
|
|
147
147
|
hint[:remediation].each do |r|
|
|
148
|
-
Log.log.info{"#{'HINT:'.bg_green.gray.blink.freeze} #{r}"}
|
|
148
|
+
Log.log.info { "#{'HINT:'.bg_green.gray.blink.freeze} #{r}" }
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
end
|
data/lib/aspera/cli/http.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'aspera/log'
|
|
|
5
5
|
require 'aspera/assert'
|
|
6
6
|
require 'aspera/line_logger'
|
|
7
7
|
require 'aspera/schema/registry'
|
|
8
|
+
require 'aspera/cli/option_declarator'
|
|
8
9
|
require 'aspera/rest'
|
|
9
10
|
require 'aspera/oauth'
|
|
10
11
|
require 'aspera/ssl'
|
|
@@ -16,6 +17,8 @@ module Aspera
|
|
|
16
17
|
# Extracted from Plugins::Config so it can be referenced independently
|
|
17
18
|
# via Context#http_config without coupling to the plugin machinery.
|
|
18
19
|
class Http
|
|
20
|
+
extend OptionDeclarator
|
|
21
|
+
|
|
19
22
|
# Certificate file extensions recognized when scanning a folder
|
|
20
23
|
CERT_EXT = %w[crt cer pem der].freeze
|
|
21
24
|
# OpenSSL constant that disables peer verification (VERIFY_NONE)
|
|
@@ -23,6 +26,13 @@ module Aspera
|
|
|
23
26
|
|
|
24
27
|
private_constant :CERT_EXT, :SELF_SIGNED_CERT
|
|
25
28
|
|
|
29
|
+
option :insecure, description: 'HTTP/S: Do not validate any certificate', allowed: Type::BOOLEAN, default: false
|
|
30
|
+
option :ignore_certificate, description: 'HTTP/S: Do not validate certificate for these URLs', allowed: [Array, NilClass]
|
|
31
|
+
option :warn_insecure, description: 'HTTP/S: Issue a warning if certificate is ignored', allowed: Type::BOOLEAN, default: true
|
|
32
|
+
option :cert_stores, description: 'HTTP/S: List of folder with trusted certificates', allowed: Type::STRING_ARRAY
|
|
33
|
+
option :http_options, schema: Schema::Registry::HTTP_OPTIONS
|
|
34
|
+
option :http_proxy, description: 'HTTP/S: URL for proxy with optional credentials'
|
|
35
|
+
|
|
26
36
|
def initialize
|
|
27
37
|
@insecure = false
|
|
28
38
|
@warn_insecure = true
|
|
@@ -36,23 +46,6 @@ module Aspera
|
|
|
36
46
|
attr_accessor :insecure, :warn_insecure
|
|
37
47
|
attr_reader :ignore_cert_host_port, :http_options
|
|
38
48
|
|
|
39
|
-
class << self
|
|
40
|
-
# Declare all HTTP/S CLI options (metadata only - no handler binding yet).
|
|
41
|
-
# Called once from Config#initialize before this instance is available as a target.
|
|
42
|
-
# Handlers are bound in a second pass via bind_options once the instance exists.
|
|
43
|
-
# @param options [Aspera::Cli::Parser] CLI options manager to declare options into
|
|
44
|
-
# @return [nil]
|
|
45
|
-
def declare_options(options)
|
|
46
|
-
options.declare(:insecure, description: 'HTTP/S: Do not validate any certificate', allowed: Allowed::TYPES_BOOLEAN, default: false)
|
|
47
|
-
options.declare(:ignore_certificate, description: 'HTTP/S: Do not validate certificate for these URLs', allowed: [Array, NilClass])
|
|
48
|
-
options.declare(:warn_insecure, description: 'HTTP/S: Issue a warning if certificate is ignored', allowed: Allowed::TYPES_BOOLEAN, default: true)
|
|
49
|
-
options.declare(:cert_stores, description: 'HTTP/S: List of folder with trusted certificates', allowed: Allowed::TYPES_STRING_ARRAY)
|
|
50
|
-
options.declare(:http_options, schema: Schema::Registry::HTTP_OPTIONS)
|
|
51
|
-
options.declare(:http_proxy, description: 'HTTP/S: URL for proxy with optional credentials')
|
|
52
|
-
nil
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
49
|
# Bind all HTTP options to this instance using set_handler.
|
|
57
50
|
# Called from Config#initialize immediately after Http.new.
|
|
58
51
|
# @param options [Aspera::Cli::Parser]
|
|
@@ -113,7 +106,7 @@ module Aspera
|
|
|
113
106
|
def ignore_cert_host_port=(url_list)
|
|
114
107
|
url_list.each do |url|
|
|
115
108
|
uri = URI.parse(url)
|
|
116
|
-
Aspera.assert(uri.scheme.eql?('https')){"Expecting https scheme: #{url}"}
|
|
109
|
+
Aspera.assert(uri.scheme.eql?('https')) { "Expecting https scheme: #{url}" }
|
|
117
110
|
@ignore_cert_host_port.push([uri.host, uri.port].freeze)
|
|
118
111
|
end
|
|
119
112
|
end
|
|
@@ -126,11 +119,11 @@ module Aspera
|
|
|
126
119
|
if ignore_cert && @warn_insecure
|
|
127
120
|
base_url = "https://#{address}:#{port}"
|
|
128
121
|
unless @ssl_warned_urls.include?(base_url)
|
|
129
|
-
Log.log.warn{"Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production."}
|
|
122
|
+
Log.log.warn { "Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production." }
|
|
130
123
|
@ssl_warned_urls.push(base_url)
|
|
131
124
|
end
|
|
132
125
|
end
|
|
133
|
-
Log.log.debug{"ignore cert? #{endpoint} -> #{ignore_cert}"}
|
|
126
|
+
Log.log.debug { "ignore cert? #{endpoint} -> #{ignore_cert}" }
|
|
134
127
|
ignore_cert
|
|
135
128
|
end
|
|
136
129
|
|
|
@@ -142,21 +135,21 @@ module Aspera
|
|
|
142
135
|
# @param path_list [Array<String>] list of file/folder paths to add to the certificate store
|
|
143
136
|
# @return [nil]
|
|
144
137
|
def trusted_cert_locations=(path_list)
|
|
145
|
-
Aspera.assert_type(path_list, Array){'cert locations'}
|
|
138
|
+
Aspera.assert_type(path_list, Array) { 'cert locations' }
|
|
146
139
|
if @certificate_store.nil?
|
|
147
140
|
Log.log.debug('Creating SSL Cert store')
|
|
148
141
|
@certificate_store = OpenSSL::X509::Store.new
|
|
149
142
|
@certificate_paths = []
|
|
150
143
|
end
|
|
151
144
|
path_list.each do |path|
|
|
152
|
-
Aspera.assert_type(path, String){'Expecting a String for certificate location'}
|
|
145
|
+
Aspera.assert_type(path, String) { 'Expecting a String for certificate location' }
|
|
153
146
|
paths_to_add = [path]
|
|
154
|
-
Log.log.debug{"Adding cert location: #{path}"}
|
|
147
|
+
Log.log.debug { "Adding cert location: #{path}" }
|
|
155
148
|
if path.eql?(SpecialValues::DEF)
|
|
156
149
|
@certificate_store.set_default_paths
|
|
157
150
|
paths_to_add = [OpenSSL::X509::DEFAULT_CERT_DIR]
|
|
158
151
|
paths_to_add.push(OpenSSL::X509::DEFAULT_CERT_FILE) unless defined?(JRUBY_VERSION)
|
|
159
|
-
paths_to_add.select!{ |f| File.exist?(f)}
|
|
152
|
+
paths_to_add.select! { |f| File.exist?(f) }
|
|
160
153
|
elsif File.file?(path)
|
|
161
154
|
@certificate_store.add_file(path)
|
|
162
155
|
elsif File.directory?(path)
|
|
@@ -168,9 +161,9 @@ module Aspera
|
|
|
168
161
|
pp = [File.realpath(p)]
|
|
169
162
|
if File.directory?(p)
|
|
170
163
|
pp = Dir.entries(p)
|
|
171
|
-
.map{ |e| File.realpath(File.join(p, e))}
|
|
172
|
-
.select{ |entry| File.file?(entry)}
|
|
173
|
-
.select{ |entry| CERT_EXT.any?{ |ext| entry.end_with?(ext)}}
|
|
164
|
+
.map { |e| File.realpath(File.join(p, e)) }
|
|
165
|
+
.select { |entry| File.file?(entry) }
|
|
166
|
+
.select { |entry| CERT_EXT.any? { |ext| entry.end_with?(ext) } }
|
|
174
167
|
end
|
|
175
168
|
@certificate_paths.concat(pp)
|
|
176
169
|
end
|
|
@@ -202,13 +195,13 @@ module Aspera
|
|
|
202
195
|
http_session.set_debug_output(LineLogger.new(:trace2)) if Log.instance.logger.trace2?
|
|
203
196
|
http_session.verify_mode = SELF_SIGNED_CERT if http_session.use_ssl? && ignore_cert?(http_session.address, http_session.port)
|
|
204
197
|
http_session.cert_store = @certificate_store if @certificate_store
|
|
205
|
-
Log.log.debug{"Using cert store #{http_session.cert_store} (#{@certificate_store})"} unless http_session.cert_store.nil?
|
|
198
|
+
Log.log.debug { "Using cert store #{http_session.cert_store} (#{@certificate_store})" } unless http_session.cert_store.nil?
|
|
206
199
|
@http_options.each do |k, v|
|
|
207
200
|
method = "#{k}=".to_sym
|
|
208
201
|
if http_session.respond_to?(method)
|
|
209
202
|
http_session.send(method, v)
|
|
210
203
|
else
|
|
211
|
-
Log.log.error{"Unknown HTTP session attribute: #{k}"}
|
|
204
|
+
Log.log.error { "Unknown HTTP session attribute: #{k}" }
|
|
212
205
|
end
|
|
213
206
|
end
|
|
214
207
|
nil
|
data/lib/aspera/cli/mailer.rb
CHANGED
|
@@ -32,16 +32,16 @@ module Aspera
|
|
|
32
32
|
# Change keys from string into symbol
|
|
33
33
|
smtp = smtp.symbolize_keys
|
|
34
34
|
unsupported = smtp.keys - SMTP_CONF_PARAMS
|
|
35
|
-
Aspera.assert(unsupported.empty?, type: Cli::Error){"Unsupported SMTP parameter: #{unsupported.join(', ')}, use: #{SMTP_CONF_PARAMS.join(', ')}"}
|
|
35
|
+
Aspera.assert(unsupported.empty?, type: Cli::Error) { "Unsupported SMTP parameter: #{unsupported.join(', ')}, use: #{SMTP_CONF_PARAMS.join(', ')}" }
|
|
36
36
|
# Boolean fields must be actual booleans, not strings like "false"
|
|
37
37
|
SMTP_BOOL_PARAMS.each do |k|
|
|
38
|
-
Aspera.assert_values(smtp[k], [true, false], type: Cli::Error){"smtp.#{k}"} if smtp.key?(k)
|
|
38
|
+
Aspera.assert_values(smtp[k], [true, false], type: Cli::Error) { "smtp.#{k}" } if smtp.key?(k)
|
|
39
39
|
end
|
|
40
40
|
SMTP_INT_PARAMS.each do |k|
|
|
41
|
-
Aspera.assert_type(smtp[k], Integer, type: Cli::Error){"smtp.#{k}"} if smtp.key?(k)
|
|
41
|
+
Aspera.assert_type(smtp[k], Integer, type: Cli::Error) { "smtp.#{k}" } if smtp.key?(k)
|
|
42
42
|
end
|
|
43
43
|
SMTP_STR_PARAMS.each do |k|
|
|
44
|
-
Aspera.assert_type(smtp[k], String, type: Cli::Error){"smtp.#{k}"} if smtp.key?(k)
|
|
44
|
+
Aspera.assert_type(smtp[k], String, type: Cli::Error) { "smtp.#{k}" } if smtp.key?(k)
|
|
45
45
|
end
|
|
46
46
|
# smtp[:ssl] = nil (false)
|
|
47
47
|
smtp[:tls] = !smtp[:ssl] unless smtp.key?(:tls)
|
|
@@ -56,9 +56,9 @@ module Aspera
|
|
|
56
56
|
smtp[:from_name] ||= smtp[:from_email].sub(/@.*$/, '').gsub(/[^a-zA-Z]/, ' ').capitalize if smtp.key?(:username)
|
|
57
57
|
smtp[:domain] ||= smtp[:from_email].sub(/^.*@/, '') if smtp.key?(:from_email)
|
|
58
58
|
%i[server port domain].each do |n|
|
|
59
|
-
Aspera.assert(smtp.key?(n)){"Missing mandatory smtp parameter: #{n}"}
|
|
59
|
+
Aspera.assert(smtp.key?(n)) { "Missing mandatory smtp parameter: #{n}" }
|
|
60
60
|
end
|
|
61
|
-
Log.
|
|
61
|
+
Log.dump(:smtp, smtp.reject { |k, _| k == :password })
|
|
62
62
|
return smtp
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -72,7 +72,7 @@ module Aspera
|
|
|
72
72
|
values[:from_name] ||= mail_conf[:from_name]
|
|
73
73
|
values[:from_email] ||= mail_conf[:from_email]
|
|
74
74
|
%i[to from_email].each do |n|
|
|
75
|
-
Aspera.assert_type(values[n], String){"Missing email parameter: #{n} in config"}
|
|
75
|
+
Aspera.assert_type(values[n], String) { "Missing email parameter: #{n} in config" }
|
|
76
76
|
end
|
|
77
77
|
start_options = [mail_conf[:domain]]
|
|
78
78
|
start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
|
data/lib/aspera/cli/mcp_tool.rb
CHANGED
|
@@ -6,11 +6,13 @@ require 'json'
|
|
|
6
6
|
require 'aspera/log'
|
|
7
7
|
require 'aspera/cli/runner'
|
|
8
8
|
require 'aspera/cli/error'
|
|
9
|
+
require 'aspera/schema/reader'
|
|
10
|
+
require 'aspera/schema/registry'
|
|
9
11
|
unless defined?(MCP::Tool)
|
|
10
12
|
begin
|
|
11
13
|
require 'mcp'
|
|
12
14
|
rescue LoadError
|
|
13
|
-
raise Cli::Error, "The 'mcp'
|
|
15
|
+
raise Cli::Error, "The 'mcp' and 'rack' gems are required. Install them with: gem install mcp rack"
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
@@ -36,14 +38,16 @@ module Aspera
|
|
|
36
38
|
Element 0 : plugin name (aoc, faspex5, node, server, config, …).
|
|
37
39
|
Elements 1+: sub-commands, then --option=value flags in any order.
|
|
38
40
|
Passing structured values: use an extended-value prefix on the relevant element:
|
|
39
|
-
"@json:{...}" — inline JSON object or array (
|
|
41
|
+
"@json:{...}" — inline JSON object or array (preferred for LLMs: no shell quoting, natural JSON)
|
|
40
42
|
"@preset:name" — expand a saved credential preset
|
|
41
43
|
"@env:VAR" — read value from environment variable
|
|
42
44
|
"@file:/path" — read value from a file
|
|
45
|
+
Note: the dot-path form (@: key=value ...) is designed for humans typing in a terminal shell.
|
|
46
|
+
Prefer @json: when building args programmatically or as an LLM.
|
|
43
47
|
|
|
44
48
|
AUTOMATIC FLAGS
|
|
45
49
|
The server automatically prepends extra_args to every call (default:
|
|
46
|
-
|
|
50
|
+
#{DEFAULT_EXTRA_ARGS.join(' ')}).
|
|
47
51
|
Do NOT repeat these flags in your args array — they are already injected.
|
|
48
52
|
If you explicitly need to override one (e.g. --interactive=yes), include it
|
|
49
53
|
in your args; your value will take precedence because it appears after the
|
|
@@ -175,10 +179,33 @@ module Aspera
|
|
|
175
179
|
else
|
|
176
180
|
MCP::Tool::Response.new([{type: 'text', text: result.data.to_s}])
|
|
177
181
|
end
|
|
182
|
+
rescue Cli::SchemaRequest => e
|
|
183
|
+
schema_path = e.path
|
|
184
|
+
if schema_path.nil?
|
|
185
|
+
MCP::Tool::Response.new([{type: 'text', text: "#{e.class}: #{e.message} (no schema available)"}], error: true)
|
|
186
|
+
else
|
|
187
|
+
rows = schema_to_rows(Schema::Registry.instance.reader(schema_path))
|
|
188
|
+
structured = {items: rows}
|
|
189
|
+
MCP::Tool::Response.new([{type: 'text', text: JSON.generate(rows)}], structured_content: structured)
|
|
190
|
+
end
|
|
178
191
|
rescue SystemExit => e
|
|
179
192
|
MCP::Tool::Response.new([{type: 'text', text: "exited with status #{e.status}"}], error: !e.status.zero?)
|
|
180
193
|
rescue => e
|
|
181
|
-
MCP::Tool::Response.new([{type: 'text', text: "#{e.class}: #{e.message}"}], error: true)
|
|
194
|
+
MCP::Tool::Response.new([{type: 'text', text: "#{e.class}: #{dedupe_lines(e.message)}"}], error: true)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Delegate to Schema::Reader#to_rows — semantic fields, no ANSI, MCP/JSON-ready.
|
|
198
|
+
def schema_to_rows(reader)
|
|
199
|
+
reader.to_rows
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Collapse consecutive duplicate lines in a multi-line message.
|
|
203
|
+
# Each run of identical lines is replaced by one line + "(×N)" suffix when N > 1.
|
|
204
|
+
def dedupe_lines(msg)
|
|
205
|
+
return msg unless msg.include?("\n")
|
|
206
|
+
msg.split("\n").chunk_while { |a, b| a == b }.map do |group|
|
|
207
|
+
group.size > 1 ? "#{group.first} (x#{group.size})" : group.first
|
|
208
|
+
end.join("\n")
|
|
182
209
|
end
|
|
183
210
|
|
|
184
211
|
# Returns the largest prefix of +items+ whose JSON serialization fits within +max_bytes+.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/cli/parser'
|
|
4
|
+
require 'aspera/cli/command_spec'
|
|
5
|
+
|
|
6
|
+
module Aspera
|
|
7
|
+
module Cli
|
|
8
|
+
# Mixin providing declarative CLI option definitions (`option :name, ...`)
|
|
9
|
+
# and batch declaration onto a Parser instance (`declare_options(parser)`).
|
|
10
|
+
module OptionDeclarator
|
|
11
|
+
# Registry of OptionSpec objects defined on this class/module.
|
|
12
|
+
# @return [Hash{Symbol => OptionSpec}]
|
|
13
|
+
def option_specs
|
|
14
|
+
@option_specs ||= {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Declare an option in this class's registry.
|
|
18
|
+
#
|
|
19
|
+
# @param name [Symbol] Option name
|
|
20
|
+
# @param description [String, nil] User-facing description
|
|
21
|
+
# @param short [String, nil] Single-character short form
|
|
22
|
+
# @param allowed [Object, nil] Allowed values
|
|
23
|
+
# @param default [Object, nil] Default value
|
|
24
|
+
# @param handler [Symbol, Hash, nil] Handler (Symbol or Hash)
|
|
25
|
+
# @param deprecation [String, nil] Deprecation message
|
|
26
|
+
# @param schema [String, nil] Schema reference
|
|
27
|
+
def option(name, description: nil,
|
|
28
|
+
short: nil, allowed: nil, default: nil,
|
|
29
|
+
handler: nil, deprecation: nil, schema: nil)
|
|
30
|
+
raise ArgumentError, "Duplicate option: #{name.inspect}" if option_specs.key?(name)
|
|
31
|
+
option_specs[name] = OptionSpec.new(
|
|
32
|
+
name: name,
|
|
33
|
+
description: description,
|
|
34
|
+
short: short,
|
|
35
|
+
allowed: allowed,
|
|
36
|
+
default: default,
|
|
37
|
+
handler: handler,
|
|
38
|
+
deprecation: deprecation,
|
|
39
|
+
schema: schema
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Declare all options registered on this class onto a Parser instance.
|
|
44
|
+
# Skips options already declared on the parser.
|
|
45
|
+
#
|
|
46
|
+
# @param parser [Aspera::Cli::Parser]
|
|
47
|
+
# @param target [Object, nil] default target object for Symbol handlers (defaults to self)
|
|
48
|
+
# @return [void]
|
|
49
|
+
def declare_options(parser, target: self)
|
|
50
|
+
option_specs.each_value do |spec|
|
|
51
|
+
next if parser.option_declared?(spec.name)
|
|
52
|
+
resolved_handler =
|
|
53
|
+
case spec.handler
|
|
54
|
+
when Symbol then {o: target, m: spec.handler}
|
|
55
|
+
when Hash then spec.handler
|
|
56
|
+
end
|
|
57
|
+
parser.declare(
|
|
58
|
+
spec.name,
|
|
59
|
+
description: spec.description,
|
|
60
|
+
short: spec.short,
|
|
61
|
+
allowed: spec.allowed,
|
|
62
|
+
default: spec.default,
|
|
63
|
+
handler: resolved_handler,
|
|
64
|
+
deprecation: spec.deprecation,
|
|
65
|
+
schema: schema_for_spec(spec)
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def schema_for_spec(spec)
|
|
73
|
+
spec.schema
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|