aspera-cli 4.26.2 → 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 +310 -21
- data/CONTRIBUTING.md +99 -14
- data/TODO.md +50 -0
- data/bin/ascli +3 -3
- data/docs/README.md +10977 -0
- data/docs/test-mcp-with-ai.md +202 -0
- data/lib/aspera/agent/base.rb +29 -7
- data/lib/aspera/agent/connect.rb +58 -21
- data/lib/aspera/agent/desktop.rb +53 -18
- data/lib/aspera/agent/direct.rb +71 -40
- data/lib/aspera/agent/factory.rb +3 -3
- data/lib/aspera/agent/httpgw.rb +55 -14
- data/lib/aspera/agent/node.rb +34 -2
- data/lib/aspera/agent/transferd.rb +48 -12
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +42 -44
- data/lib/aspera/api/cos_node.rb +10 -9
- data/lib/aspera/api/faspex.rb +16 -15
- data/lib/aspera/api/httpgw.rb +23 -23
- data/lib/aspera/api/node.rb +51 -70
- data/lib/aspera/api/queries/bss_subscription_account.graphql +18 -0
- data/lib/aspera/api/queries/bss_subscription_usage.graphql +18 -0
- data/lib/aspera/ascmd.rb +32 -30
- data/lib/aspera/ascp/installation.rb +75 -69
- data/lib/aspera/ascp/management.rb +7 -8
- data/lib/aspera/assert.rb +12 -13
- data/lib/aspera/cli/ascp_actions.rb +155 -0
- data/lib/aspera/cli/async_transfer_store.rb +125 -0
- data/lib/aspera/cli/bootstrapper.rb +197 -0
- data/lib/aspera/cli/command_registry.rb +145 -0
- data/lib/aspera/cli/command_spec.rb +141 -0
- data/lib/aspera/cli/context.rb +19 -6
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +27 -26
- data/lib/aspera/cli/formatter.rb +95 -59
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +3 -3
- data/lib/aspera/cli/http.rb +74 -30
- data/lib/aspera/cli/info.rb +2 -0
- data/lib/aspera/cli/mailer.rb +97 -0
- data/lib/aspera/cli/mcp_tool.rb +225 -0
- data/lib/aspera/cli/option_declarator.rb +77 -0
- data/lib/aspera/cli/options.schema.yaml +634 -7
- data/lib/aspera/cli/parser.rb +1217 -0
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1178 -881
- data/lib/aspera/cli/plugins/ats.rb +205 -162
- data/lib/aspera/cli/plugins/base.rb +703 -175
- data/lib/aspera/cli/plugins/basic_auth.rb +7 -9
- data/lib/aspera/cli/plugins/config.rb +434 -757
- data/lib/aspera/cli/plugins/console.rb +107 -65
- data/lib/aspera/cli/plugins/cos.rb +46 -33
- data/lib/aspera/cli/plugins/factory.rb +6 -6
- data/lib/aspera/cli/plugins/faspex5.rb +627 -388
- data/lib/aspera/cli/plugins/faspio.rb +54 -51
- data/lib/aspera/cli/plugins/httpgw.rb +18 -25
- data/lib/aspera/cli/plugins/mcp.rb +279 -0
- data/lib/aspera/cli/plugins/node.rb +988 -840
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +112 -135
- data/lib/aspera/cli/plugins/preview.rb +195 -157
- data/lib/aspera/cli/plugins/server.rb +141 -91
- data/lib/aspera/cli/plugins/shares.rb +343 -110
- data/lib/aspera/cli/preset_actions.rb +159 -0
- data/lib/aspera/cli/preset_manager.rb +82 -39
- data/lib/aspera/cli/result.rb +78 -26
- data/lib/aspera/cli/runner.rb +323 -153
- data/lib/aspera/cli/secret_finder.rb +40 -0
- data/lib/aspera/cli/special_values.rb +1 -0
- data/lib/aspera/cli/sync_actions.rb +89 -64
- data/lib/aspera/cli/terminal_formatter.rb +3 -3
- data/lib/aspera/cli/transfer_actions.rb +90 -0
- data/lib/aspera/cli/transfer_agent.rb +129 -64
- data/lib/aspera/cli/transfer_progress.rb +9 -9
- data/lib/aspera/cli/vault_manager.rb +79 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +28 -30
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +34 -34
- data/lib/aspera/command_line_converter.rb +1 -1
- data/lib/aspera/coverage.rb +1 -2
- data/lib/aspera/data_repository.rb +2 -1
- data/lib/aspera/dot_container.rb +12 -12
- data/lib/aspera/environment.rb +30 -23
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +5 -5
- data/lib/aspera/faspex_postproc.rb +16 -10
- data/lib/aspera/graphql.rb +37 -0
- data/lib/aspera/hash_ext.rb +8 -2
- data/lib/aspera/json_rpc/client.rb +62 -0
- data/lib/aspera/json_rpc/version.rb +7 -0
- data/lib/aspera/keychain/base.rb +10 -3
- data/lib/aspera/keychain/encrypted_hash.rb +92 -17
- data/lib/aspera/keychain/factory.rb +20 -8
- 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 +82 -0
- data/lib/aspera/log.rb +29 -15
- data/lib/aspera/markdown.rb +90 -7
- data/lib/aspera/nagios.rb +8 -8
- data/lib/aspera/node_simulator.rb +35 -27
- data/lib/aspera/oauth/base.rb +9 -10
- data/lib/aspera/oauth/boot.rb +5 -5
- data/lib/aspera/oauth/factory.rb +12 -10
- data/lib/aspera/oauth/jwt.rb +9 -11
- data/lib/aspera/oauth/web.rb +6 -6
- data/lib/aspera/persistency_action_once.rb +12 -12
- data/lib/aspera/persistency_folder.rb +18 -11
- data/lib/aspera/preview/file_types.rb +5 -5
- data/lib/aspera/preview/generator.rb +52 -49
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +5 -5
- data/lib/aspera/preview/utils.rb +66 -19
- data/lib/aspera/products/connect.rb +2 -2
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +3 -3
- data/lib/aspera/products/transferd.rb +4 -4
- data/lib/aspera/proxy_auto_config.rb +15 -14
- data/lib/aspera/rest.rb +159 -151
- data/lib/aspera/rest_error_analyzer.rb +6 -6
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +11 -10
- 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 on Cloud API-0.2.6-enhanced.yaml +1853 -510
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +4653 -0
- data/lib/aspera/schema/documentation.rb +65 -28
- data/lib/aspera/schema/reader.rb +136 -13
- data/lib/aspera/schema/registry.rb +60 -8
- data/lib/aspera/secret_hider.rb +12 -7
- data/lib/aspera/ssh.rb +64 -31
- data/lib/aspera/ssl.rb +5 -5
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/database.rb +2 -2
- data/lib/aspera/sync/operations.rb +22 -25
- data/lib/aspera/temp_file_manager.rb +25 -5
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +36 -34
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +10 -10
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +12 -3
- data/lib/aspera/uri_reader.rb +52 -15
- data/lib/aspera/web_auth.rb +8 -7
- data/lib/aspera/web_server_simple.rb +15 -12
- data/lib/aspera/yaml.rb +5 -4
- data.tar.gz.sig +0 -0
- metadata +34 -6
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/manager.rb +0 -766
- data/lib/aspera/cli/plugins/faspex.rb +0 -539
- data/lib/aspera/json_rpc.rb +0 -52
- data/lib/aspera/transfer/uri.rb +0 -56
data/lib/aspera/cli/error.rb
CHANGED
|
@@ -4,9 +4,25 @@ module Aspera
|
|
|
4
4
|
module Cli
|
|
5
5
|
# CLI base exception
|
|
6
6
|
class Error < StandardError; end
|
|
7
|
+
|
|
8
|
+
# Raised by dispatch_from_registry when --help is encountered mid-dispatch.
|
|
9
|
+
# Carries the plugin instance (with #help_path set) so the runner can display
|
|
10
|
+
# context-sensitive help without unwinding through a full rescue chain.
|
|
11
|
+
class HelpRequest < StandardError
|
|
12
|
+
# @return [Plugins::Base]
|
|
13
|
+
attr_reader :plugin
|
|
14
|
+
|
|
15
|
+
def initialize(plugin)
|
|
16
|
+
super('help requested')
|
|
17
|
+
@plugin = plugin
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
7
21
|
# Raised when an unexpected argument is provided.
|
|
8
22
|
class BadArgument < Error; end
|
|
23
|
+
|
|
9
24
|
class MissingArgument < Error; end
|
|
25
|
+
|
|
10
26
|
class NoSuchElement < Error; end
|
|
11
27
|
|
|
12
28
|
# Raised when a lookup for a specific entity fails to return exactly one result.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# cspell:ignore csvt jsonpp stdbin
|
|
4
4
|
require 'aspera/uri_reader'
|
|
5
5
|
require 'aspera/environment'
|
|
6
|
+
require 'aspera/yaml'
|
|
6
7
|
require 'aspera/log'
|
|
7
8
|
require 'aspera/assert'
|
|
8
9
|
require 'aspera/cli/error'
|
|
@@ -67,7 +68,7 @@ module Aspera
|
|
|
67
68
|
# @param value [String] The value as parameter
|
|
68
69
|
# @param ext_type [Symbol] The method of extended value
|
|
69
70
|
def assert_no_value(value, ext_type)
|
|
70
|
-
Aspera.assert(value.empty?, type: BadArgument){"no value allowed for extended value type: #{ext_type}"}
|
|
71
|
+
Aspera.assert(value.empty?, type: BadArgument) { "no value allowed for extended value type: #{ext_type}" }
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
def read_stdin(mode)
|
|
@@ -88,25 +89,25 @@ module Aspera
|
|
|
88
89
|
# e.g. `preset` is reader in config plugin
|
|
89
90
|
# @type [Hash{Symbol => Proc}]
|
|
90
91
|
@handlers = {
|
|
91
|
-
val: lambda{ |i| i},
|
|
92
|
-
base64: lambda{ |i| Base64.decode64(i)},
|
|
93
|
-
csvt: lambda{ |i| ExtendedValue.decode_csvt(i)},
|
|
94
|
-
env: lambda{ |i| ENV.fetch(i, nil)},
|
|
95
|
-
file: lambda{ |i| File.read(File.expand_path(i))},
|
|
96
|
-
uri: lambda{ |i| UriReader.read(i)},
|
|
97
|
-
json: lambda{ |i| ExtendedValue.JSON_parse(i)},
|
|
98
|
-
lines: lambda{ |i| i.split("\n")},
|
|
99
|
-
list: lambda{ |i| i[1..].split(i[0])},
|
|
100
|
-
none: lambda{ |i| ExtendedValue.assert_no_value(i, :none); nil}, # rubocop:disable Style/Semicolon
|
|
101
|
-
path: lambda{ |i| File.expand_path(i)},
|
|
102
|
-
re: lambda{ |i| Regexp.new(i, Regexp::MULTILINE)},
|
|
103
|
-
ruby: lambda{ |i| Environment.secure_eval(i, __FILE__, __LINE__)},
|
|
92
|
+
val: lambda { |i| i },
|
|
93
|
+
base64: lambda { |i| Base64.decode64(i) },
|
|
94
|
+
csvt: lambda { |i| ExtendedValue.decode_csvt(i) },
|
|
95
|
+
env: lambda { |i| ENV.fetch(i, nil) },
|
|
96
|
+
file: lambda { |i| File.read(File.expand_path(i)) },
|
|
97
|
+
uri: lambda { |i| UriReader.read(i) },
|
|
98
|
+
json: lambda { |i| ExtendedValue.JSON_parse(i) },
|
|
99
|
+
lines: lambda { |i| i.split("\n") },
|
|
100
|
+
list: lambda { |i| i[1..].split(i[0]) },
|
|
101
|
+
none: lambda { |i| ExtendedValue.assert_no_value(i, :none); nil }, # rubocop:disable Style/Semicolon
|
|
102
|
+
path: lambda { |i| File.expand_path(i) },
|
|
103
|
+
re: lambda { |i| Regexp.new(i, Regexp::MULTILINE) },
|
|
104
|
+
ruby: lambda { |i| Environment.secure_eval(i, __FILE__, __LINE__) },
|
|
104
105
|
s: lambda(&:to_s),
|
|
105
|
-
secret: lambda{ |i| prompt = i.empty? ? 'secret' : i; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
|
|
106
|
-
stdin: lambda{ |i| ExtendedValue.read_stdin(i)},
|
|
107
|
-
yaml: lambda{ |i| YAML.
|
|
108
|
-
zlib: lambda{ |i| Zlib::Inflate.inflate(i)},
|
|
109
|
-
extend: lambda{ |i| ExtendedValue.instance.evaluate_extend(i)}
|
|
106
|
+
secret: lambda { |i| prompt = i.empty? ? 'secret' : i; $stdin.getpass("#{prompt}> ") }, # rubocop:disable Style/Semicolon
|
|
107
|
+
stdin: lambda { |i| ExtendedValue.read_stdin(i) },
|
|
108
|
+
yaml: lambda { |i| YAML.safe_load(i) },
|
|
109
|
+
zlib: lambda { |i| Zlib::Inflate.inflate(i) },
|
|
110
|
+
extend: lambda { |i| ExtendedValue.instance.evaluate_extend(i) }
|
|
110
111
|
}
|
|
111
112
|
@regex_single = nil
|
|
112
113
|
@regex_extend = nil
|
|
@@ -126,7 +127,7 @@ module Aspera
|
|
|
126
127
|
attr_reader :default_decoder
|
|
127
128
|
|
|
128
129
|
def default_decoder=(value)
|
|
129
|
-
Log.log.debug{"Setting default decoder to (#{value.class}) #{value}"}
|
|
130
|
+
Log.log.debug { "Setting default decoder to (#{value.class}) #{value}" }
|
|
130
131
|
Aspera.assert_values(value, DEFAULT_DECODERS)
|
|
131
132
|
value = nil if value.eql?(:none)
|
|
132
133
|
@default_decoder = value
|
|
@@ -137,9 +138,9 @@ module Aspera
|
|
|
137
138
|
|
|
138
139
|
# Add a new handler
|
|
139
140
|
def on(name, &block)
|
|
140
|
-
Aspera.assert_type(name, Symbol){'name'}
|
|
141
|
-
Aspera.assert(block){"block required when registering handler for #{name}"}
|
|
142
|
-
Log.log.debug{"Setting handler for #{name}"}
|
|
141
|
+
Aspera.assert_type(name, Symbol) { 'name' }
|
|
142
|
+
Aspera.assert(block) { "block required when registering handler for #{name}" }
|
|
143
|
+
Log.log.debug { "Setting handler for #{name}" }
|
|
143
144
|
@handlers[name] = block
|
|
144
145
|
update_regex
|
|
145
146
|
end
|
|
@@ -155,7 +156,7 @@ module Aspera
|
|
|
155
156
|
return value unless value.is_a?(String)
|
|
156
157
|
Aspera.assert_array_all(allowed, Class) unless allowed.nil?
|
|
157
158
|
# use default decoder if not an extended value and expect complex types
|
|
158
|
-
using_default_decoder = allowed&.all?{ |t| DEFAULT_PARSER_TYPES.include?(t)} && !@regex_single.match?(value) && !@default_decoder.nil?
|
|
159
|
+
using_default_decoder = allowed&.all? { |t| DEFAULT_PARSER_TYPES.include?(t) } && !@regex_single.match?(value) && !@default_decoder.nil?
|
|
159
160
|
value = [MARKER_START, @default_decoder, MARKER_END, value].join if using_default_decoder
|
|
160
161
|
# First determine decoders, in reversed order
|
|
161
162
|
handlers_reversed = []
|
|
@@ -165,7 +166,7 @@ module Aspera
|
|
|
165
166
|
value = m[2]
|
|
166
167
|
break if SPECIAL_HANDLERS.include?(handler)
|
|
167
168
|
end
|
|
168
|
-
Log.log.trace1{"evaluating: #{handlers_reversed}, value: #{value}"}
|
|
169
|
+
Log.log.trace1 { "evaluating: #{handlers_reversed}, value: #{value.class}:#{value}" }
|
|
169
170
|
handlers_reversed.each do |handler|
|
|
170
171
|
value = @handlers[handler].call(value)
|
|
171
172
|
rescue => e
|
|
@@ -179,7 +180,7 @@ module Aspera
|
|
|
179
180
|
def evaluate_extend(value)
|
|
180
181
|
while (m = value.match(@regex_extend))
|
|
181
182
|
sub_value = "@#{m[2]}:#{m[3]}"
|
|
182
|
-
Log.log.debug{"evaluating #{sub_value}"}
|
|
183
|
+
Log.log.debug { "evaluating #{sub_value}" }
|
|
183
184
|
value = "#{m[1]}#{evaluate(sub_value, context: 'composite extended value')}#{m[4]}"
|
|
184
185
|
end
|
|
185
186
|
return value
|
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# cspell:ignore jsonpp
|
|
4
|
+
require 'aspera/schema/registry'
|
|
5
|
+
require 'aspera/cli/option_declarator'
|
|
4
6
|
require 'aspera/cli/special_values'
|
|
5
7
|
require 'aspera/cli/terminal_formatter'
|
|
6
8
|
require 'aspera/preview/terminal'
|
|
@@ -21,6 +23,33 @@ module Aspera
|
|
|
21
23
|
module Cli
|
|
22
24
|
# Take care of CLI output on terminal
|
|
23
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
|
+
|
|
24
53
|
class << self
|
|
25
54
|
# Replace special values with a readable version on terminal
|
|
26
55
|
def replace_specific_for_terminal(input_hash, string_list_separator)
|
|
@@ -57,7 +86,7 @@ module Aspera
|
|
|
57
86
|
end
|
|
58
87
|
|
|
59
88
|
def all_but(list)
|
|
60
|
-
Array(list).map{ |i| "#{FIELDS_LESS}#{i}"}.unshift(SpecialValues::ALL)
|
|
89
|
+
Array(list).map { |i| "#{FIELDS_LESS}#{i}" }.unshift(SpecialValues::ALL)
|
|
61
90
|
end
|
|
62
91
|
end
|
|
63
92
|
|
|
@@ -108,32 +137,50 @@ module Aspera
|
|
|
108
137
|
end
|
|
109
138
|
end
|
|
110
139
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
options.declare(:table_style, '(Table) Display style', allowed: [Hash], handler: {o: self, m: :option_handler}, default: {})
|
|
122
|
-
options.declare(:flat_hash, '(Table) Display deep values as additional keys', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_handler}, default: true)
|
|
123
|
-
options.declare(
|
|
124
|
-
:multi_single, '(Table) Control how object list is displayed as single table, or multiple objects', allowed: %i[no yes single],
|
|
125
|
-
handler: {o: self, m: :option_handler}, default: :no
|
|
126
|
-
)
|
|
127
|
-
options.declare(:show_secrets, 'Show secrets on command output', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_handler}, default: false)
|
|
128
|
-
options.declare(:image, 'Options for image display', allowed: Hash, handler: {o: self, m: :option_handler}, default: {})
|
|
140
|
+
# Bind all formatter options to this instance using set_handler.
|
|
141
|
+
# Called from Runner after Formatter.new.
|
|
142
|
+
# @param options [Aspera::Cli::Parser]
|
|
143
|
+
# @return [nil]
|
|
144
|
+
def bind_options(options)
|
|
145
|
+
@parser = options
|
|
146
|
+
%i[out display format output fields select table_style flat_hash multi_single show_secrets image].each do |opt|
|
|
147
|
+
options.set_handler(opt, object: self, method: :option_handler)
|
|
148
|
+
end
|
|
149
|
+
nil
|
|
129
150
|
end
|
|
130
151
|
|
|
131
|
-
#
|
|
132
|
-
#
|
|
152
|
+
# Getter/setter handler called by the option manager for all formatter options
|
|
153
|
+
# @param option_symbol [Symbol] Option name (one of :out, :format, :output, :display, :fields, :select, :table_style, :flat_hash, :multi_single, :show_secrets, :image)
|
|
154
|
+
# @param operation [Symbol] :get or :set
|
|
155
|
+
# @param value [Object, nil] Value to set (only used when operation is :set)
|
|
156
|
+
# @return [Object, nil] Current option value when operation is :get; nil otherwise
|
|
133
157
|
def option_handler(option_symbol, operation, value = nil)
|
|
134
158
|
Aspera.assert_values(operation, %i[set get])
|
|
135
159
|
case operation
|
|
136
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
|
|
137
184
|
@options[option_symbol] = value
|
|
138
185
|
# special handling of some options
|
|
139
186
|
case option_symbol
|
|
@@ -147,12 +194,14 @@ module Aspera
|
|
|
147
194
|
end
|
|
148
195
|
when :image
|
|
149
196
|
# get list if key arguments of method
|
|
150
|
-
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] }
|
|
151
198
|
# check that only supported options are given
|
|
152
199
|
unknown_options = value.keys.map(&:to_sym) - allowed_options
|
|
153
|
-
|
|
200
|
+
Aspera.assert(unknown_options.empty?) { "Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" }
|
|
154
201
|
end
|
|
155
|
-
when :get
|
|
202
|
+
when :get
|
|
203
|
+
return if option_symbol.eql?(:out)
|
|
204
|
+
return @options[option_symbol]
|
|
156
205
|
else Aspera.error_unreachable_line
|
|
157
206
|
end
|
|
158
207
|
nil
|
|
@@ -163,7 +212,7 @@ module Aspera
|
|
|
163
212
|
# @param message_level [Symbol] The level of the message - must be one of: :data, :info, :error
|
|
164
213
|
# @param message [String] The message to display
|
|
165
214
|
# @param hide_secrets [Boolean] Whether to hide secrets in the message (default: true)
|
|
166
|
-
# @return [
|
|
215
|
+
# @return [nil]
|
|
167
216
|
# @note Message display behavior depends on the message_level:
|
|
168
217
|
# - +:data+ messages are displayed unless display level is +:error+
|
|
169
218
|
# - +:info+ messages are only displayed when display level is +:info+
|
|
@@ -198,9 +247,9 @@ module Aspera
|
|
|
198
247
|
# @param result [Result] Result object to display
|
|
199
248
|
def display_results(result)
|
|
200
249
|
require 'aspera/cli/result'
|
|
201
|
-
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' }
|
|
202
251
|
|
|
203
|
-
Log.log.debug{"display_results: result class=#{result.class.name}"}
|
|
252
|
+
Log.log.debug { "display_results: result class=#{result.class.name}" }
|
|
204
253
|
Log.dump(:data, result.data, level: :trace1)
|
|
205
254
|
Log.dump(:fields, result.fields, level: :trace1)
|
|
206
255
|
|
|
@@ -218,7 +267,7 @@ module Aspera
|
|
|
218
267
|
end
|
|
219
268
|
|
|
220
269
|
# Set the format type (used by Image result)
|
|
221
|
-
def
|
|
270
|
+
def format_type=(format)
|
|
222
271
|
@options[:format] = format
|
|
223
272
|
end
|
|
224
273
|
|
|
@@ -242,16 +291,16 @@ module Aspera
|
|
|
242
291
|
@options[:image].symbolize_keys
|
|
243
292
|
end
|
|
244
293
|
|
|
245
|
-
# @return all fields of all objects in list of objects
|
|
294
|
+
# @return [Array<String>] all fields of all objects in list of objects
|
|
246
295
|
def all_fields(data)
|
|
247
|
-
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
|
|
248
297
|
end
|
|
249
298
|
|
|
250
|
-
# @return the list of fields to display
|
|
299
|
+
# @return [Array<String>] the list of fields to display
|
|
251
300
|
# @param data [Array<Hash>] data to display
|
|
252
301
|
# @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
|
|
253
302
|
def compute_fields(data, default)
|
|
254
|
-
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}" }
|
|
255
304
|
Log.dump(:compute_fields_default, default, level: :trace1)
|
|
256
305
|
# the requested list of fields, but if can contain special values
|
|
257
306
|
request =
|
|
@@ -259,8 +308,8 @@ module Aspera
|
|
|
259
308
|
# when NilClass then [SpecialValues::DEF]
|
|
260
309
|
when String then @options[:fields].split(',')
|
|
261
310
|
when Array then @options[:fields]
|
|
262
|
-
when Regexp then return all_fields(data).select{ |i| i.match(@options[:fields])}
|
|
263
|
-
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) }
|
|
264
313
|
else Aspera.error_unexpected_value(@options[:fields])
|
|
265
314
|
end
|
|
266
315
|
Aspera.assert_array_all(request, String)
|
|
@@ -277,12 +326,12 @@ module Aspera
|
|
|
277
326
|
# get the list of all column names used in all lines, not just first one, as all lines may have different columns
|
|
278
327
|
request.unshift(*all_fields(data))
|
|
279
328
|
when SpecialValues::DEF
|
|
280
|
-
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)
|
|
281
330
|
default = all_fields(data) if default.nil?
|
|
282
331
|
request.unshift(*default)
|
|
283
332
|
else
|
|
284
333
|
if removal
|
|
285
|
-
result = result.reject{ |i| i.eql?(item)}
|
|
334
|
+
result = result.reject { |i| i.eql?(item) }
|
|
286
335
|
else
|
|
287
336
|
result.push(item)
|
|
288
337
|
end
|
|
@@ -298,12 +347,12 @@ module Aspera
|
|
|
298
347
|
return data unless data.is_a?(Array)
|
|
299
348
|
# by default, keep all data intact
|
|
300
349
|
return data if @options[:fields].eql?(SpecialValues::DEF) && @options[:select].nil?
|
|
301
|
-
Aspera.assert_array_all(data, Hash){'filter or select'}
|
|
350
|
+
Aspera.assert_array_all(data, Hash) { 'filter or select' }
|
|
302
351
|
filter_columns_on_select(data)
|
|
303
352
|
return data if @options[:fields].eql?(SpecialValues::DEF)
|
|
304
353
|
selected_fields = compute_fields(data, @options[:fields])
|
|
305
|
-
return data.map{ |i| i[selected_fields.first]} if selected_fields.length == 1
|
|
306
|
-
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) }
|
|
307
356
|
end
|
|
308
357
|
|
|
309
358
|
# filter the list of items on the select option
|
|
@@ -312,12 +361,12 @@ module Aspera
|
|
|
312
361
|
case @options[:select]
|
|
313
362
|
when Proc
|
|
314
363
|
begin
|
|
315
|
-
data.select!{ |i| @options[:select].call(i)}
|
|
316
|
-
rescue
|
|
364
|
+
data.select! { |i| @options[:select].call(i) }
|
|
365
|
+
rescue StandardError => e
|
|
317
366
|
raise Cli::BadArgument, "Error in user-provided ruby lambda code during select: #{e.message}"
|
|
318
367
|
end
|
|
319
368
|
when Hash
|
|
320
|
-
@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) } }
|
|
321
370
|
end
|
|
322
371
|
end
|
|
323
372
|
|
|
@@ -337,7 +386,7 @@ module Aspera
|
|
|
337
386
|
format_style = @options[:table_style].symbolize_keys
|
|
338
387
|
string_list_separator = format_style.delete(:str_lst_sep) || STR_LST_SEP_VERT
|
|
339
388
|
# convert data to string, and keep only display fields
|
|
340
|
-
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) }
|
|
341
390
|
# if table has only one element, and only one field, display the value
|
|
342
391
|
if object_array.length == 1 && fields.length == 1
|
|
343
392
|
Log.log.debug("single element, field: #{fields.first}")
|
|
@@ -353,10 +402,10 @@ module Aspera
|
|
|
353
402
|
Log.dump(:object_array, object_array)
|
|
354
403
|
Log.dump(:fields, fields)
|
|
355
404
|
# convert data to string, and keep only display fields
|
|
356
|
-
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 } }
|
|
357
406
|
# remove empty rows
|
|
358
|
-
final_table_rows.select!{ |i| !(i.is_a?(Hash) && i.empty?)}
|
|
359
|
-
#
|
|
407
|
+
final_table_rows.select! { |i| !(i.is_a?(Hash) && i.empty?) }
|
|
408
|
+
# fields: list of column names to display
|
|
360
409
|
case @options[:format]
|
|
361
410
|
when :table
|
|
362
411
|
format_style[:border] = :unicode_round if Environment.terminal_supports_unicode?
|
|
@@ -392,19 +441,6 @@ module Aspera
|
|
|
392
441
|
end
|
|
393
442
|
nil
|
|
394
443
|
end
|
|
395
|
-
|
|
396
|
-
# remove a fields from the list
|
|
397
|
-
FIELDS_LESS = '-'
|
|
398
|
-
# supported output formats
|
|
399
|
-
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
|
400
|
-
# user output levels
|
|
401
|
-
DISPLAY_LEVELS = %i[info data error].freeze
|
|
402
|
-
# column names for single object display in table
|
|
403
|
-
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
404
|
-
# Terminal: vertical separator for list of strings.
|
|
405
|
-
STR_LST_SEP_VERT = "\n"
|
|
406
|
-
|
|
407
|
-
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES, :STR_LST_SEP_VERT
|
|
408
444
|
end
|
|
409
445
|
end
|
|
410
446
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/cli/version'
|
|
4
|
+
require 'aspera/cli/info'
|
|
5
|
+
require 'aspera/environment'
|
|
6
|
+
require 'aspera/persistency_action_once'
|
|
7
|
+
require 'aspera/rest'
|
|
8
|
+
require 'aspera/log'
|
|
9
|
+
require 'date'
|
|
10
|
+
|
|
11
|
+
module Aspera
|
|
12
|
+
module Cli
|
|
13
|
+
# Mixin providing gem version-check functionality to Plugin::Config.
|
|
14
|
+
# Depends on `options` and `persistency` being available in the including class.
|
|
15
|
+
module GemChecker
|
|
16
|
+
GEM_CHECK_DATE_FMT = '%Y/%m/%d'
|
|
17
|
+
|
|
18
|
+
# @return [Hash] current vs latest gem version info
|
|
19
|
+
def check_gem_version
|
|
20
|
+
latest_version =
|
|
21
|
+
begin
|
|
22
|
+
Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{Info::GEM_NAME}/latest.json")['version']
|
|
23
|
+
rescue StandardError
|
|
24
|
+
Log.log.warn('Could not retrieve latest gem version on rubygems.')
|
|
25
|
+
'0'
|
|
26
|
+
end
|
|
27
|
+
if Gem::Version.new(Environment.ruby_version) < Gem::Version.new(Info::RUBY_FUTURE_MINIMUM_VERSION)
|
|
28
|
+
Log.log.warn do
|
|
29
|
+
"Note that a future version will require Ruby version #{Info::RUBY_FUTURE_MINIMUM_VERSION} at minimum, " \
|
|
30
|
+
"you are using #{Environment.ruby_version}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
return {
|
|
34
|
+
name: Info::GEM_NAME,
|
|
35
|
+
current: Cli::VERSION,
|
|
36
|
+
latest: latest_version,
|
|
37
|
+
need_update: Gem::Version.new(Cli::VERSION) < Gem::Version.new(latest_version)
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Check periodically if a newer gem version is available; log a warning if so.
|
|
42
|
+
# Called once per run by Runner, before command execution.
|
|
43
|
+
def periodic_check_newer_gem_version
|
|
44
|
+
delay_days = options.get_option(:version_check_days, mandatory: true).to_i
|
|
45
|
+
return if delay_days.eql?(0)
|
|
46
|
+
last_check_array = []
|
|
47
|
+
check_date_persist = PersistencyActionOnce.new(
|
|
48
|
+
manager: persistency,
|
|
49
|
+
data: last_check_array,
|
|
50
|
+
id: 'version_last_check'
|
|
51
|
+
)
|
|
52
|
+
current_date = Date.today
|
|
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}" }
|
|
55
|
+
return if !last_check_days.nil? && last_check_days < delay_days
|
|
56
|
+
last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
|
|
57
|
+
check_date_persist.save
|
|
58
|
+
check_data = check_gem_version
|
|
59
|
+
Log.log.warn do
|
|
60
|
+
"A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}"
|
|
61
|
+
end if check_data[:need_update]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/aspera/cli/hints.rb
CHANGED
|
@@ -129,6 +129,7 @@ module Aspera
|
|
|
129
129
|
class << self
|
|
130
130
|
# @param error [Exception] exception object
|
|
131
131
|
def hint_for(error, formatter)
|
|
132
|
+
# Loop on known error hints
|
|
132
133
|
ERROR_HINTS.each do |hint|
|
|
133
134
|
next unless error.is_a?(hint[:exception])
|
|
134
135
|
message = error.message
|
|
@@ -141,12 +142,11 @@ module Aspera
|
|
|
141
142
|
next unless message.eql?(m)
|
|
142
143
|
when Regexp
|
|
143
144
|
next unless message.match?(m)
|
|
144
|
-
else Aspera.error_unexpected_value(m)
|
|
145
|
+
else Aspera.error_unexpected_value(m) { 'hint match' }
|
|
145
146
|
end
|
|
146
147
|
hint[:remediation].each do |r|
|
|
147
|
-
Log.log.info{"#{'HINT:'.bg_green.gray.blink.freeze} #{r}"}
|
|
148
|
+
Log.log.info { "#{'HINT:'.bg_green.gray.blink.freeze} #{r}" }
|
|
148
149
|
end
|
|
149
|
-
break
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
152
|
end
|