aspera-cli 4.27.0 → 4.27.2
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 +46 -1
- data/CONTRIBUTING.md +1 -7
- data/TODO.md +50 -0
- data/docs/README.md +709 -489
- 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 +303 -314
- 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 +17 -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 +13 -13
- 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 on Cloud API-0.2.6-enhanced.yaml +39 -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/transfer/spec.schema.yaml +1 -0
- 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/assert.rb
CHANGED
|
@@ -34,9 +34,9 @@ module Aspera
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Assert that a condition is true, else raise exception
|
|
37
|
-
# @param assertion [
|
|
38
|
-
# @param info
|
|
39
|
-
# @param type
|
|
37
|
+
# @param assertion [Boolean] Must be true
|
|
38
|
+
# @param info [String,nil] Fixed message in case assert fails, else use block
|
|
39
|
+
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log level
|
|
40
40
|
# @yieldreturn [String] A string that describes the problem for complex messages
|
|
41
41
|
# The block is executed in the context of the Aspera module
|
|
42
42
|
def assert(assertion, info = nil, type: AssertError)
|
|
@@ -45,7 +45,6 @@ module Aspera
|
|
|
45
45
|
message = 'assertion failed'
|
|
46
46
|
info = yield if block_given?
|
|
47
47
|
message = type.eql?(AssertError) ? "#{message}: #{info}" : info if info
|
|
48
|
-
# message = "#{message}: #{caller.find{ |call| !call.start_with?(__FILE__)}}"
|
|
49
48
|
report_error(type, message)
|
|
50
49
|
end
|
|
51
50
|
|
|
@@ -55,7 +54,7 @@ module Aspera
|
|
|
55
54
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
56
55
|
# @yieldreturn [String] Additional description to prepend to the error message
|
|
57
56
|
def assert_type(value, *classes, type: AssertError)
|
|
58
|
-
assert(classes.any?{ |k| value.is_a?(k)}, type: type){"#{"#{yield}: " if block_given?}expecting type #{classes.join(', ')}, but have (#{value.class})#{value.inspect}"}
|
|
57
|
+
assert(classes.any? { |k| value.is_a?(k) }, type: type) { "#{"#{yield}: " if block_given?}expecting type #{classes.join(', ')}, but have (#{value.class})#{value.inspect}" }
|
|
59
58
|
end
|
|
60
59
|
|
|
61
60
|
# Assert that all value of array are of the same specified type.
|
|
@@ -64,20 +63,20 @@ module Aspera
|
|
|
64
63
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
65
64
|
# @yieldreturn [String] Additional description to prepend to the error message
|
|
66
65
|
def assert_array_all(array, klass, type: AssertError)
|
|
67
|
-
assert_type(array, Array, type: AssertError){'array'}
|
|
68
|
-
assert(array.all?(klass), type: type){"#{"#{yield}: " if block_given?}expecting all as #{klass}, but have #{array.map(&:class).uniq}"}
|
|
66
|
+
assert_type(array, Array, type: AssertError) { 'array' }
|
|
67
|
+
assert(array.all?(klass), type: type) { "#{"#{yield}: " if block_given?}expecting all as #{klass}, but have #{array.map(&:class).uniq}" }
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# Assert value is Hash, keys have type, and Values have type
|
|
72
71
|
# @param hash [Hash] The hash to check
|
|
73
|
-
# @param key_class [Class] The expected type of keys (or nil)
|
|
74
|
-
# @param value_class [Class] The expected type of values (or nil)
|
|
72
|
+
# @param key_class [Class] The expected type of keys (or nil for no check)
|
|
73
|
+
# @param value_class [Class] The expected type of values (or nil for no check)
|
|
75
74
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
76
75
|
# @yieldreturn [String] Additional description to prepend to the error message
|
|
77
76
|
def assert_hash_all(hash, key_class, value_class, type: AssertError)
|
|
78
|
-
assert_type(hash, Hash, type: AssertError){'hash'}
|
|
79
|
-
assert_array_all(hash.keys, key_class, type: type){"#{"#{yield}: " if block_given?}keys"} unless key_class.nil?
|
|
80
|
-
assert_array_all(hash.values, value_class, type: type){"#{"#{yield}: " if block_given?}values"} unless value_class.nil?
|
|
77
|
+
assert_type(hash, Hash, type: AssertError) { 'hash' }
|
|
78
|
+
assert_array_all(hash.keys, key_class, type: type) { "#{"#{yield}: " if block_given?}keys" } unless key_class.nil?
|
|
79
|
+
assert_array_all(hash.values, value_class, type: type) { "#{"#{yield}: " if block_given?}values" } unless value_class.nil?
|
|
81
80
|
end
|
|
82
81
|
|
|
83
82
|
# Assert that value is one of the given values
|
|
@@ -86,7 +85,7 @@ module Aspera
|
|
|
86
85
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
87
86
|
# @yieldreturn [String] Additional description to prepend to the error message
|
|
88
87
|
def assert_values(value, values, type: AssertError)
|
|
89
|
-
assert_type(values, Array, type: AssertError){'values'}
|
|
88
|
+
assert_type(values, Array, type: AssertError) { 'values' }
|
|
90
89
|
assert(values.include?(value), type: type) do
|
|
91
90
|
val_list = values.inspect
|
|
92
91
|
val_list = "one of #{val_list}" if values.is_a?(Array)
|
|
@@ -27,14 +27,14 @@ module Aspera
|
|
|
27
27
|
@sdk_default_location = true
|
|
28
28
|
Log.log.debug('SDK folder is not set, checking default')
|
|
29
29
|
sdk_dir = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME)
|
|
30
|
-
Log.log.debug{"Checking: #{sdk_dir}"}
|
|
30
|
+
Log.log.debug { "Checking: #{sdk_dir}" }
|
|
31
31
|
if !Dir.exist?(sdk_dir)
|
|
32
|
-
Log.log.debug{"No such folder: #{sdk_dir}"}
|
|
32
|
+
Log.log.debug { "No such folder: #{sdk_dir}" }
|
|
33
33
|
former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: Info::CMD_NAME), TRANSFERD_APP_NAME)
|
|
34
|
-
Log.log.debug{"Checking: #{former_sdk_folder}"}
|
|
34
|
+
Log.log.debug { "Checking: #{former_sdk_folder}" }
|
|
35
35
|
sdk_dir = former_sdk_folder if Dir.exist?(former_sdk_folder)
|
|
36
36
|
end
|
|
37
|
-
Log.log.debug{"Using: #{sdk_dir}"}
|
|
37
|
+
Log.log.debug { "Using: #{sdk_dir}" }
|
|
38
38
|
Products::Transferd.sdk_directory = sdk_dir
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -59,7 +59,7 @@ module Aspera
|
|
|
59
59
|
def action_ascp_info(**)
|
|
60
60
|
data = Ascp::Installation.instance.ascp_info
|
|
61
61
|
data['ts'] = transfer.user_transfer_spec
|
|
62
|
-
DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
|
|
62
|
+
DataRepository::ELEMENTS.each_with_object(data) { |i, h| h[i.to_s] = DataRepository.instance.item(i) }
|
|
63
63
|
SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
|
|
64
64
|
Result::SingleObject.new(data)
|
|
65
65
|
end
|
|
@@ -75,7 +75,7 @@ module Aspera
|
|
|
75
75
|
|
|
76
76
|
def action_ascp_schema(agent_name: nil, **)
|
|
77
77
|
schema = Transfer::Spec::SCHEMA.current.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
|
|
78
|
-
schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent_name, v)} unless agent_name.nil?
|
|
78
|
+
schema['properties'] = schema['properties'].select { |_k, v| CommandLineBuilder.supported_by_agent(agent_name, v) } unless agent_name.nil?
|
|
79
79
|
schema['properties'] = schema['properties'].sort.to_h
|
|
80
80
|
Result::SingleObject.new(schema)
|
|
81
81
|
end
|
|
@@ -106,7 +106,7 @@ module Aspera
|
|
|
106
106
|
'short' => names[:short].to_s,
|
|
107
107
|
'parameters' => param_names
|
|
108
108
|
}
|
|
109
|
-
end.sort_by{ |r| r['name']}
|
|
109
|
+
end.sort_by { |r| r['name'] }
|
|
110
110
|
Result::ObjectList.new(rows, fields: %w[name short parameters])
|
|
111
111
|
end
|
|
112
112
|
|
|
@@ -27,18 +27,46 @@ module Aspera
|
|
|
27
27
|
CATEGORY = 'async_transfer_'
|
|
28
28
|
private_constant :CATEGORY
|
|
29
29
|
|
|
30
|
+
# Class-level registry of in-process agent references, keyed by job_id.
|
|
31
|
+
# Shared across all AsyncTransferStore instances within the same Ruby process,
|
|
32
|
+
# so that a reference registered in one Runner call is visible in the next
|
|
33
|
+
# (e.g. MCP server mode where each tool call creates a fresh Runner/Context).
|
|
34
|
+
# Never persisted to disk.
|
|
35
|
+
@agent_refs = {}
|
|
36
|
+
class << self
|
|
37
|
+
attr_reader :agent_refs
|
|
38
|
+
end
|
|
39
|
+
|
|
30
40
|
# @param persistency [PersistencyFolder]
|
|
31
41
|
def initialize(persistency)
|
|
32
42
|
@persistency = persistency
|
|
33
43
|
end
|
|
34
44
|
|
|
45
|
+
# Register an in-process agent reference for a job.
|
|
46
|
+
# Called by TransferAgent immediately after start_transfer (async mode).
|
|
47
|
+
# The reference is only available while the same Ruby process is alive.
|
|
48
|
+
# @param job_id [String] the ascli-generated UUID
|
|
49
|
+
# @param agent_ref [Object] live agent instance (responds to sessions_by_job)
|
|
50
|
+
def register_agent_ref(job_id, agent_ref)
|
|
51
|
+
self.class.agent_refs[job_id] = agent_ref
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Retrieve a previously registered in-process agent reference.
|
|
55
|
+
# @param job_id [String]
|
|
56
|
+
# @return [Object, nil] the live agent instance, or nil if not registered / process restarted
|
|
57
|
+
def agent_ref(job_id)
|
|
58
|
+
self.class.agent_refs[job_id]
|
|
59
|
+
end
|
|
60
|
+
|
|
35
61
|
# Persist (create or update) an async transfer entry.
|
|
62
|
+
# Keys whose name starts with '_' are in-process references (e.g. '_agent_ref')
|
|
63
|
+
# and are intentionally excluded from the JSON serialization — at every nesting level.
|
|
36
64
|
# @param job_id [String] the ascli-generated UUID
|
|
37
65
|
# @param data [Hash] fields to store (will be JSON-serialised)
|
|
38
66
|
def write(job_id, data)
|
|
39
|
-
Aspera.assert_type(job_id, String){'job_id'}
|
|
40
|
-
Aspera.assert_type(data, Hash){'data'}
|
|
41
|
-
@persistency.put(store_key(job_id), JSON.generate(data))
|
|
67
|
+
Aspera.assert_type(job_id, String) { 'job_id' }
|
|
68
|
+
Aspera.assert_type(data, Hash) { 'data' }
|
|
69
|
+
@persistency.put(store_key(job_id), JSON.generate(strip_internal_keys(data)))
|
|
42
70
|
nil
|
|
43
71
|
end
|
|
44
72
|
|
|
@@ -46,7 +74,7 @@ module Aspera
|
|
|
46
74
|
# @param job_id [String]
|
|
47
75
|
# @return [Hash, nil] the stored data, or nil if not found
|
|
48
76
|
def read(job_id)
|
|
49
|
-
Aspera.assert_type(job_id, String){'job_id'}
|
|
77
|
+
Aspera.assert_type(job_id, String) { 'job_id' }
|
|
50
78
|
raw = @persistency.get(store_key(job_id))
|
|
51
79
|
return if raw.nil?
|
|
52
80
|
JSON.parse(raw)
|
|
@@ -65,7 +93,7 @@ module Aspera
|
|
|
65
93
|
# Delete one entry.
|
|
66
94
|
# @param job_id [String]
|
|
67
95
|
def delete(job_id)
|
|
68
|
-
Aspera.assert_type(job_id, String){'job_id'}
|
|
96
|
+
Aspera.assert_type(job_id, String) { 'job_id' }
|
|
69
97
|
@persistency.delete(store_key(job_id))
|
|
70
98
|
nil
|
|
71
99
|
end
|
|
@@ -76,6 +104,22 @@ module Aspera
|
|
|
76
104
|
def store_key(job_id)
|
|
77
105
|
"#{CATEGORY}#{job_id}"
|
|
78
106
|
end
|
|
107
|
+
|
|
108
|
+
# Recursively remove keys whose name starts with '_' from a Hash.
|
|
109
|
+
# Such keys hold in-process Ruby object references (e.g. '_agent_ref') that
|
|
110
|
+
# cannot be serialized to JSON and must never reach the file system.
|
|
111
|
+
def strip_internal_keys(value)
|
|
112
|
+
case value
|
|
113
|
+
when Hash
|
|
114
|
+
value.each_with_object({}) do |(k, v), h|
|
|
115
|
+
h[k] = strip_internal_keys(v) unless k.to_s.start_with?('_')
|
|
116
|
+
end
|
|
117
|
+
when Array
|
|
118
|
+
value.map { |v| strip_internal_keys(v) }
|
|
119
|
+
else
|
|
120
|
+
value
|
|
121
|
+
end
|
|
122
|
+
end
|
|
79
123
|
end
|
|
80
124
|
end
|
|
81
125
|
end
|
|
@@ -66,7 +66,7 @@ module Aspera
|
|
|
66
66
|
|
|
67
67
|
# @param context [Context] the shared context to populate
|
|
68
68
|
def initialize(context)
|
|
69
|
-
Aspera.assert_type(context, Context){'context'}
|
|
69
|
+
Aspera.assert_type(context, Context) { 'context' }
|
|
70
70
|
@context = context
|
|
71
71
|
@pac_exec = nil
|
|
72
72
|
end
|
|
@@ -101,7 +101,7 @@ module Aspera
|
|
|
101
101
|
default: default_app_main_folder(app_name: Info::CMD_NAME)
|
|
102
102
|
)
|
|
103
103
|
@context.options.parse_options!
|
|
104
|
-
Log.log.debug{"#{Info::CMD_NAME} folder: #{@context.main_folder}"}
|
|
104
|
+
Log.log.debug { "#{Info::CMD_NAME} folder: #{@context.main_folder}" }
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
# context.persistency + plugin lookup folders
|
|
@@ -130,20 +130,20 @@ module Aspera
|
|
|
130
130
|
@context.options.declare(:vault_password, description: 'Vault password')
|
|
131
131
|
# Register @preset and @vault handlers BEFORE parse_options! so that
|
|
132
132
|
# values like --secret=@preset:name are correctly evaluated at parse time.
|
|
133
|
-
ExtendedValue.instance.on(EXTEND_PRESET){ |v| @context.presets.by_name(v)}
|
|
133
|
+
ExtendedValue.instance.on(EXTEND_PRESET) { |v| @context.presets.by_name(v) }
|
|
134
134
|
ExtendedValue.instance.on(EXTEND_VAULT, &vault_value_cb)
|
|
135
135
|
@context.options.parse_options!
|
|
136
136
|
# Load global config default preset (equivalent of add_plugin_default_preset(:config))
|
|
137
137
|
default_config_name = @context.presets.plugin_default_name(CONF_GLOBAL_SYM)
|
|
138
138
|
unless default_config_name.nil?
|
|
139
|
-
Log.log.debug{"add_plugin_default_preset:#{CONF_GLOBAL_SYM}:#{default_config_name}"}
|
|
139
|
+
Log.log.debug { "add_plugin_default_preset:#{CONF_GLOBAL_SYM}:#{default_config_name}" }
|
|
140
140
|
@context.options.add_option_preset(@context.presets.by_name(default_config_name), 'default_plugin', override: false)
|
|
141
141
|
end
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
# Declare + parse :progress_bar: sets context.progress_bar
|
|
145
145
|
def setup_progress_bar
|
|
146
|
-
@context.options.declare(:progress_bar, description: 'Display progress bar', allowed:
|
|
146
|
+
@context.options.declare(:progress_bar, description: 'Display progress bar', allowed: Type::BOOLEAN, default: Environment.terminal?)
|
|
147
147
|
@context.options.parse_options!
|
|
148
148
|
@context.progress_bar = TransferProgress.new if @context.options.get_option(:progress_bar)
|
|
149
149
|
end
|
|
@@ -159,7 +159,7 @@ module Aspera
|
|
|
159
159
|
@context.pac_executor = ProxyAutoConfig.new(pac_script).register_uri_generic
|
|
160
160
|
proxy_user_pass = @context.options.get_option(:proxy_credentials)
|
|
161
161
|
if proxy_user_pass
|
|
162
|
-
Aspera.assert(proxy_user_pass.length.eql?(2), type: Cli::BadArgument){"proxy_credentials shall have two elements (#{proxy_user_pass.length})"}
|
|
162
|
+
Aspera.assert(proxy_user_pass.length.eql?(2), type: Cli::BadArgument) { "proxy_credentials shall have two elements (#{proxy_user_pass.length})" }
|
|
163
163
|
@context.pac_executor.proxy_user = proxy_user_pass[0]
|
|
164
164
|
@context.pac_executor.proxy_pass = proxy_user_pass[1]
|
|
165
165
|
end
|
|
@@ -171,8 +171,8 @@ module Aspera
|
|
|
171
171
|
def setup_rest_and_transfer_runtime
|
|
172
172
|
RestParameters.instance.user_agent = Info::CMD_NAME
|
|
173
173
|
RestParameters.instance.progress_bar = @context.progress_bar
|
|
174
|
-
RestParameters.instance.session_cb = ->(http_session){@context.http_config.update_session(http_session)}
|
|
175
|
-
RestParameters.instance.spinner_cb = ->(title = nil, action: :spin){@context.formatter.long_operation(title, action: action)}
|
|
174
|
+
RestParameters.instance.session_cb = ->(http_session) { @context.http_config.update_session(http_session) }
|
|
175
|
+
RestParameters.instance.spinner_cb = ->(title = nil, action: :spin) { @context.formatter.long_operation(title, action: action) }
|
|
176
176
|
OAuth::Web.additional_info = "#{Info::CMD_NAME} v#{Cli::VERSION}"
|
|
177
177
|
Transfer::Parameters.file_list_folder = File.join(@context.main_folder, FILE_LIST_FOLDER_NAME)
|
|
178
178
|
RestErrorAnalyzer.instance.log_file = File.join(@context.main_folder, REST_EXCEPTIONS_LOG_FILENAME)
|
|
@@ -182,7 +182,7 @@ module Aspera
|
|
|
182
182
|
# @return [String] ~/.aspera
|
|
183
183
|
def module_family_folder
|
|
184
184
|
user_home_folder = Dir.home
|
|
185
|
-
Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error){"Home folder does not exist: #{user_home_folder}. Check your user environment."}
|
|
185
|
+
Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error) { "Home folder does not exist: #{user_home_folder}. Check your user environment." }
|
|
186
186
|
File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
|
|
187
187
|
end
|
|
188
188
|
|
|
@@ -118,7 +118,7 @@ module Aspera
|
|
|
118
118
|
# Rule: leaf commands with no explicit action must have a matching instance method
|
|
119
119
|
next if spec.action # explicit action: skip
|
|
120
120
|
next if @children_index[path]&.any? # intermediate node: skip
|
|
121
|
-
next if spec.delegates_to
|
|
121
|
+
next if spec.delegates_to # delegated: skip
|
|
122
122
|
next unless plugin_class
|
|
123
123
|
implicit_method = CommandSpec.action_method(path)
|
|
124
124
|
unless plugin_class.method_defined?(implicit_method) || plugin_class.private_method_defined?(implicit_method)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'aspera/schema/registry'
|
|
4
|
+
|
|
3
5
|
module Aspera
|
|
4
6
|
module Cli
|
|
5
7
|
# Declares a positional argument consumed by a command.
|
|
@@ -14,7 +16,10 @@ module Aspera
|
|
|
14
16
|
# @!attribute default [Object, nil] Default value when mandatory: false and no argument provided
|
|
15
17
|
# @!attribute schema [String, nil] JSON schema name for validation and --help introspection
|
|
16
18
|
# @!attribute bulk [Boolean] When true, wraps read+loop for bulk mode (Array if --bulk yes)
|
|
17
|
-
# @!attribute lookup [Symbol, nil]
|
|
19
|
+
# @!attribute lookup [Symbol, Proc, nil] Percent-selector resolver (only used when type: :identifier).
|
|
20
|
+
# Symbol → resolved via send(lookup, field, value, **ctx).
|
|
21
|
+
# Proc/lambda → called via instance_exec(field, value, **ctx, &lookup).
|
|
22
|
+
# Style: use Symbol for named methods; ->(){} for 1-liners; lambda do…end for 2–3 statements.
|
|
18
23
|
# @!attribute allowed [Array<Symbol>, nil] Allowed Symbol values; when set, type is forced to Symbol and accept_list is applied
|
|
19
24
|
# @!attribute interactive [Boolean] When true, sets ask_missing_mandatory before resolving so interactive prompting is triggered when no CLI args are provided
|
|
20
25
|
ArgumentSpec = Struct.new(
|
|
@@ -81,7 +86,6 @@ module Aspera
|
|
|
81
86
|
# @!attribute delegates_to [Symbol, Array<Symbol>, nil] Re-enter the command tree at this path
|
|
82
87
|
# @!attribute delegate_instance [Symbol, nil] Instance method returning a different plugin object
|
|
83
88
|
# @!attribute aliases [Array<Symbol>, nil] Alternative names accepted for this command (each resolves to this command's id)
|
|
84
|
-
# @!attribute entity_execute [Hash, nil] Shorthand: expand to Base#entity_execute with these parameters
|
|
85
89
|
# @!attribute transfer_paths [:send, :receive, nil] File-list resolution delegated to TransferAgent; mutually exclusive with arguments
|
|
86
90
|
# @!attribute condition [Symbol, nil] Instance method returning Boolean; if false command is hidden from dispatch
|
|
87
91
|
# @!attribute query_schema [String, nil] Schema path for --query help; when set, the runner hints `--query=help`
|
|
@@ -96,7 +100,6 @@ module Aspera
|
|
|
96
100
|
:delegates_to,
|
|
97
101
|
:delegate_instance,
|
|
98
102
|
:aliases,
|
|
99
|
-
:entity_execute,
|
|
100
103
|
:transfer_paths,
|
|
101
104
|
:condition,
|
|
102
105
|
:query_schema,
|
data/lib/aspera/cli/context.rb
CHANGED
|
@@ -40,7 +40,7 @@ module Aspera
|
|
|
40
40
|
# Initialize all members to nil, so that they are defined and can be validated later
|
|
41
41
|
# @return [nil]
|
|
42
42
|
def initialize
|
|
43
|
-
MEMBERS.each{ |i| instance_variable_set(:"@#{i}", nil)}
|
|
43
|
+
MEMBERS.each { |i| instance_variable_set(:"@#{i}", nil) }
|
|
44
44
|
@progress_bar = nil
|
|
45
45
|
@pac_executor = nil
|
|
46
46
|
end
|
|
@@ -50,8 +50,8 @@ module Aspera
|
|
|
50
50
|
# @return [nil]
|
|
51
51
|
def validate
|
|
52
52
|
MEMBERS.each do |i|
|
|
53
|
-
Aspera.assert(instance_variable_defined?(:"@#{i}")){"context member @#{i} is not defined"}
|
|
54
|
-
Aspera.assert(!instance_variable_get(:"@#{i}").nil?){"context member @#{i} is nil"}
|
|
53
|
+
Aspera.assert(instance_variable_defined?(:"@#{i}")) { "context member @#{i} is not defined" }
|
|
54
|
+
Aspera.assert(!instance_variable_get(:"@#{i}").nil?) { "context member @#{i} is nil" }
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -68,7 +68,7 @@ module Aspera
|
|
|
68
68
|
# @param value [String] The value as parameter
|
|
69
69
|
# @param ext_type [Symbol] The method of extended value
|
|
70
70
|
def assert_no_value(value, ext_type)
|
|
71
|
-
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}" }
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def read_stdin(mode)
|
|
@@ -89,25 +89,25 @@ module Aspera
|
|
|
89
89
|
# e.g. `preset` is reader in config plugin
|
|
90
90
|
# @type [Hash{Symbol => Proc}]
|
|
91
91
|
@handlers = {
|
|
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__)},
|
|
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__) },
|
|
105
105
|
s: lambda(&:to_s),
|
|
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)}
|
|
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) }
|
|
111
111
|
}
|
|
112
112
|
@regex_single = nil
|
|
113
113
|
@regex_extend = nil
|
|
@@ -127,7 +127,7 @@ module Aspera
|
|
|
127
127
|
attr_reader :default_decoder
|
|
128
128
|
|
|
129
129
|
def default_decoder=(value)
|
|
130
|
-
Log.log.debug{"Setting default decoder to (#{value.class}) #{value}"}
|
|
130
|
+
Log.log.debug { "Setting default decoder to (#{value.class}) #{value}" }
|
|
131
131
|
Aspera.assert_values(value, DEFAULT_DECODERS)
|
|
132
132
|
value = nil if value.eql?(:none)
|
|
133
133
|
@default_decoder = value
|
|
@@ -138,9 +138,9 @@ module Aspera
|
|
|
138
138
|
|
|
139
139
|
# Add a new handler
|
|
140
140
|
def on(name, &block)
|
|
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}"}
|
|
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}" }
|
|
144
144
|
@handlers[name] = block
|
|
145
145
|
update_regex
|
|
146
146
|
end
|
|
@@ -156,7 +156,7 @@ module Aspera
|
|
|
156
156
|
return value unless value.is_a?(String)
|
|
157
157
|
Aspera.assert_array_all(allowed, Class) unless allowed.nil?
|
|
158
158
|
# use default decoder if not an extended value and expect complex types
|
|
159
|
-
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?
|
|
160
160
|
value = [MARKER_START, @default_decoder, MARKER_END, value].join if using_default_decoder
|
|
161
161
|
# First determine decoders, in reversed order
|
|
162
162
|
handlers_reversed = []
|
|
@@ -166,7 +166,7 @@ module Aspera
|
|
|
166
166
|
value = m[2]
|
|
167
167
|
break if SPECIAL_HANDLERS.include?(handler)
|
|
168
168
|
end
|
|
169
|
-
Log.log.trace1{"evaluating: #{handlers_reversed}, value: #{value}"}
|
|
169
|
+
Log.log.trace1 { "evaluating: #{handlers_reversed}, value: #{value.class}:#{value}" }
|
|
170
170
|
handlers_reversed.each do |handler|
|
|
171
171
|
value = @handlers[handler].call(value)
|
|
172
172
|
rescue => e
|
|
@@ -180,7 +180,7 @@ module Aspera
|
|
|
180
180
|
def evaluate_extend(value)
|
|
181
181
|
while (m = value.match(@regex_extend))
|
|
182
182
|
sub_value = "@#{m[2]}:#{m[3]}"
|
|
183
|
-
Log.log.debug{"evaluating #{sub_value}"}
|
|
183
|
+
Log.log.debug { "evaluating #{sub_value}" }
|
|
184
184
|
value = "#{m[1]}#{evaluate(sub_value, context: 'composite extended value')}#{m[4]}"
|
|
185
185
|
end
|
|
186
186
|
return value
|