aspera-cli 4.13.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +81 -7
  4. data/CONTRIBUTING.md +22 -6
  5. data/README.md +2038 -1080
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/dascli +1 -1
  9. data/examples/proxy.pac +1 -1
  10. data/examples/rubyc +24 -0
  11. data/lib/aspera/aoc.rb +219 -159
  12. data/lib/aspera/ascmd.rb +25 -14
  13. data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
  14. data/lib/aspera/cli/error.rb +17 -0
  15. data/lib/aspera/cli/extended_value.rb +47 -12
  16. data/lib/aspera/cli/formatter.rb +260 -179
  17. data/lib/aspera/cli/hints.rb +80 -0
  18. data/lib/aspera/cli/main.rb +104 -156
  19. data/lib/aspera/cli/manager.rb +259 -209
  20. data/lib/aspera/cli/plugin.rb +123 -63
  21. data/lib/aspera/cli/plugins/alee.rb +2 -3
  22. data/lib/aspera/cli/plugins/aoc.rb +341 -261
  23. data/lib/aspera/cli/plugins/ats.rb +22 -21
  24. data/lib/aspera/cli/plugins/bss.rb +5 -5
  25. data/lib/aspera/cli/plugins/config.rb +578 -627
  26. data/lib/aspera/cli/plugins/console.rb +44 -6
  27. data/lib/aspera/cli/plugins/cos.rb +15 -17
  28. data/lib/aspera/cli/plugins/faspex.rb +114 -100
  29. data/lib/aspera/cli/plugins/faspex5.rb +411 -264
  30. data/lib/aspera/cli/plugins/node.rb +354 -259
  31. data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
  32. data/lib/aspera/cli/plugins/preview.rb +82 -90
  33. data/lib/aspera/cli/plugins/server.rb +79 -32
  34. data/lib/aspera/cli/plugins/shares.rb +55 -42
  35. data/lib/aspera/cli/sync_actions.rb +68 -0
  36. data/lib/aspera/cli/transfer_agent.rb +66 -73
  37. data/lib/aspera/cli/transfer_progress.rb +74 -0
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/colors.rb +12 -8
  40. data/lib/aspera/command_line_builder.rb +14 -11
  41. data/lib/aspera/cos_node.rb +3 -2
  42. data/lib/aspera/data/6 +0 -0
  43. data/lib/aspera/environment.rb +24 -9
  44. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  45. data/lib/aspera/fasp/agent_base.rb +31 -77
  46. data/lib/aspera/fasp/agent_connect.rb +25 -21
  47. data/lib/aspera/fasp/agent_direct.rb +89 -103
  48. data/lib/aspera/fasp/agent_httpgw.rb +231 -149
  49. data/lib/aspera/fasp/agent_node.rb +41 -34
  50. data/lib/aspera/fasp/agent_trsdk.rb +75 -32
  51. data/lib/aspera/fasp/error_info.rb +4 -2
  52. data/lib/aspera/fasp/faux_file.rb +52 -0
  53. data/lib/aspera/fasp/installation.rb +53 -195
  54. data/lib/aspera/fasp/management.rb +244 -0
  55. data/lib/aspera/fasp/parameters.rb +71 -37
  56. data/lib/aspera/fasp/parameters.yaml +76 -8
  57. data/lib/aspera/fasp/products.rb +162 -0
  58. data/lib/aspera/fasp/resume_policy.rb +3 -3
  59. data/lib/aspera/fasp/transfer_spec.rb +7 -6
  60. data/lib/aspera/fasp/uri.rb +26 -24
  61. data/lib/aspera/faspex_gw.rb +2 -2
  62. data/lib/aspera/faspex_postproc.rb +2 -2
  63. data/lib/aspera/hash_ext.rb +14 -4
  64. data/lib/aspera/json_rpc.rb +49 -0
  65. data/lib/aspera/keychain/macos_security.rb +13 -13
  66. data/lib/aspera/line_logger.rb +23 -0
  67. data/lib/aspera/log.rb +58 -16
  68. data/lib/aspera/node.rb +157 -92
  69. data/lib/aspera/oauth.rb +37 -19
  70. data/lib/aspera/open_application.rb +4 -4
  71. data/lib/aspera/persistency_action_once.rb +1 -1
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/file_types.rb +4 -2
  74. data/lib/aspera/preview/generator.rb +22 -35
  75. data/lib/aspera/preview/options.rb +2 -0
  76. data/lib/aspera/preview/terminal.rb +73 -16
  77. data/lib/aspera/preview/utils.rb +21 -28
  78. data/lib/aspera/proxy_auto_config.js +2 -2
  79. data/lib/aspera/rest.rb +136 -68
  80. data/lib/aspera/rest_call_error.rb +1 -1
  81. data/lib/aspera/rest_error_analyzer.rb +15 -14
  82. data/lib/aspera/rest_errors_aspera.rb +37 -34
  83. data/lib/aspera/secret_hider.rb +18 -15
  84. data/lib/aspera/ssh.rb +5 -2
  85. data/lib/aspera/sync.rb +127 -119
  86. data/lib/aspera/temp_file_manager.rb +10 -3
  87. data/lib/aspera/web_auth.rb +10 -7
  88. data/lib/aspera/web_server_simple.rb +9 -4
  89. data.tar.gz.sig +0 -0
  90. metadata +34 -17
  91. metadata.gz.sig +0 -0
  92. data/docs/test_env.conf +0 -186
  93. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  94. data/lib/aspera/cli/listener/logger.rb +0 -22
  95. data/lib/aspera/cli/listener/progress.rb +0 -50
  96. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  97. data/lib/aspera/cli/plugins/sync.rb +0 -44
  98. data/lib/aspera/data/7 +0 -0
  99. data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/fasp/transfer_spec'
4
- require 'aspera/cli/listener/logger'
5
- require 'aspera/cli/listener/progress_multi'
6
4
  require 'aspera/cli/info'
7
5
 
8
6
  module Aspera
@@ -16,7 +14,7 @@ module Aspera
16
14
  # special value for --sources : read file list from transfer spec (--ts)
17
15
  FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
18
16
  FILE_LIST_OPTIONS = [FILE_LIST_FROM_ARGS, FILE_LIST_FROM_TRANSFER_SPEC, 'Array'].freeze
19
- DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
17
+ DEFAULT_TRANSFER_NOTIFY_TEMPLATE = <<~END_OF_TEMPLATE
20
18
  From: <%=from_name%> <<%=from_email%>>
21
19
  To: <<%=to%>>
22
20
  Subject: <%=subject%>
@@ -29,7 +27,7 @@ module Aspera
29
27
  private_constant :FILE_LIST_FROM_ARGS,
30
28
  :FILE_LIST_FROM_TRANSFER_SPEC,
31
29
  :FILE_LIST_OPTIONS,
32
- :DEFAULT_TRANSFER_NOTIF_TMPL
30
+ :DEFAULT_TRANSFER_NOTIFY_TEMPLATE
33
31
  TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
34
32
 
35
33
  class << self
@@ -43,81 +41,79 @@ module Aspera
43
41
  end
44
42
 
45
43
  # @param env external objects: option manager, config file manager
46
- def initialize(opt_mgr, config)
44
+ def initialize(opt_mgr, config_plugin)
47
45
  @opt_mgr = opt_mgr
48
- @config = config
46
+ @config = config_plugin
49
47
  # command line can override transfer spec
50
- @transfer_spec_cmdline = {'create_dir' => true}
48
+ @transfer_spec_command_line = {'create_dir' => true}
49
+ # options for transfer agent
51
50
  @transfer_info = {}
52
51
  # the currently selected transfer agent
53
52
  @agent = nil
54
- @progress_listener = Listener::ProgressMulti.new
55
53
  # source/destination pair, like "paths" of transfer spec
56
54
  @transfer_paths = nil
57
- @opt_mgr.set_obj_attr(:ts, self, :option_transfer_spec)
58
- @opt_mgr.set_obj_attr(:transfer_info, self, :option_transfer_info)
59
- @opt_mgr.add_opt_simple(:ts, "Override transfer spec values (Hash, e.g. use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
60
- @opt_mgr.add_opt_simple(:to_folder, 'Destination folder for transferred files')
61
- @opt_mgr.add_opt_simple(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
62
- @opt_mgr.add_opt_list(:src_type, %i[list pair], 'Type of file list')
63
- @opt_mgr.add_opt_list(:transfer, TRANSFER_AGENTS, 'Type of transfer agent')
64
- @opt_mgr.add_opt_simple(:transfer_info, 'Parameters for transfer agent (Hash)')
65
- @opt_mgr.add_opt_list(:progress, %i[none native multi], 'Type of progress bar')
66
- @opt_mgr.set_option(:transfer, :direct)
67
- @opt_mgr.set_option(:src_type, :list)
68
- @opt_mgr.set_option(:progress, :native) # use native ascp progress bar as it is more reliable
55
+ @opt_mgr.declare(:ts, 'Override transfer spec values', types: Hash, handler: {o: self, m: :option_transfer_spec})
56
+ @opt_mgr.declare(:to_folder, 'Destination folder for transferred files')
57
+ @opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
58
+ @opt_mgr.declare(:src_type, 'Type of file list', values: %i[list pair], default: :list)
59
+ @opt_mgr.declare(:transfer, 'Type of transfer agent', values: TRANSFER_AGENTS, default: :direct)
60
+ @opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :transfer_info})
69
61
  @opt_mgr.parse_options!
70
62
  end
71
63
 
72
- def option_transfer_spec; @transfer_spec_cmdline; end
64
+ def option_transfer_spec; @transfer_spec_command_line; end
73
65
 
74
66
  # multiple option are merged
75
67
  def option_transfer_spec=(value)
76
68
  raise 'option ts shall be a Hash' unless value.is_a?(Hash)
77
- @transfer_spec_cmdline.merge!(value)
69
+ @transfer_spec_command_line.deep_merge!(value)
78
70
  end
79
71
 
80
- def option_transfer_info; @transfer_info; end
72
+ # add other transfer spec parameters
73
+ def option_transfer_spec_deep_merge(ts); @transfer_spec_command_line.deep_merge!(ts); end
81
74
 
82
- # multiple option are merged
83
- def option_transfer_info=(value)
84
- raise 'option transfer_info shall be a Hash' unless value.is_a?(Hash)
85
- @transfer_info.merge!(value)
75
+ # @return [Hash] transfer spec with updated values from command line, including removed values
76
+ def updated_ts(transfer_spec={})
77
+ transfer_spec.deep_merge!(@transfer_spec_command_line)
78
+ # recursively remove values that are nil (user wants to delete)
79
+ transfer_spec.deep_do { |hash, key, value, _unused| hash.delete(key) if value.nil?}
80
+ return transfer_spec
86
81
  end
87
82
 
88
- def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
83
+ attr_reader :transfer_info
84
+
85
+ # multiple option are merged
86
+ def transfer_info=(value)
87
+ @transfer_info.deep_merge!(value)
88
+ end
89
89
 
90
90
  def agent_instance=(instance)
91
91
  @agent = instance
92
- @agent.add_listener(Listener::Logger.new)
93
- # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
94
- if @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:multi) ||
95
- (@opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
96
- @agent.add_listener(@progress_listener)
97
- end
98
92
  end
99
93
 
100
94
  # analyze options and create new agent if not already created or set
101
95
  def set_agent_by_options
102
96
  return nil unless @agent.nil?
103
- agent_type = @opt_mgr.get_option(:transfer, is_type: :mandatory)
97
+ agent_type = @opt_mgr.get_option(:transfer, mandatory: true)
104
98
  # agent plugin is loaded on demand to avoid loading unnecessary dependencies
105
99
  require "aspera/fasp/agent_#{agent_type}"
106
- agent_options = @opt_mgr.get_option(:transfer_info)
107
- raise CliBadArgument, "the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
108
- 'e.g. use @json:<json>' unless agent_options.is_a?(Hash)
109
- # special case: use default node
110
- if agent_type.eql?(:node) && agent_options.empty?
111
- param_set_name = @config.get_plugin_default_config_name(:node)
112
- raise CliBadArgument, "No default node configured. Please specify #{Manager.option_name_to_line(:transfer_info)}" if param_set_name.nil?
113
- agent_options = @config.preset_by_name(param_set_name)
114
- end
115
- # special case: native progress bar
116
- if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native)
117
- agent_options[:quiet] = false
100
+ # set keys as symbols
101
+ agent_options = @opt_mgr.get_option(:transfer_info).symbolize_keys
102
+ # special cases
103
+ case agent_type
104
+ when :node
105
+ if agent_options.empty?
106
+ param_set_name = @config.get_plugin_default_config_name(:node)
107
+ raise Cli::BadArgument, "No default node configured. Please specify #{Manager.option_name_to_line(:transfer_info)}" if param_set_name.nil?
108
+ agent_options = @config.preset_by_name(param_set_name).symbolize_keys
109
+ end
110
+ when :direct
111
+ # by default do not display ascp native progress bar
112
+ agent_options[:quiet] = true unless agent_options.key?(:quiet)
113
+ agent_options[:check_ignore] = ->(host, port){@config.ignore_cert?(host, port)}
114
+ agent_options[:trusted_certs] = @config.trusted_cert_locations(files_only: true) unless agent_options.key?(:trusted_certs)
118
115
  end
119
- # normalize after getting from user or default node
120
- agent_options = agent_options.symbolize_keys
116
+ agent_options[:progress] = @config.progress_bar
121
117
  # get agent instance
122
118
  new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
123
119
  self.agent_instance = new_agent
@@ -131,7 +127,7 @@ module Aspera
131
127
  dest_folder = @opt_mgr.get_option(:to_folder)
132
128
  # do not expand path, if user wants to expand path: user @path:
133
129
  return dest_folder unless dest_folder.nil?
134
- dest_folder = @transfer_spec_cmdline['destination_root']
130
+ dest_folder = @transfer_spec_command_line['destination_root']
135
131
  return dest_folder unless dest_folder.nil?
136
132
  # default: / on remote, . on local
137
133
  case direction.to_s
@@ -157,7 +153,7 @@ module Aspera
157
153
  # return cache if set
158
154
  return @transfer_paths unless @transfer_paths.nil?
159
155
  # start with lower priority : get paths from transfer spec on command line
160
- @transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.key?('paths')
156
+ @transfer_paths = @transfer_spec_command_line['paths'] if @transfer_spec_command_line.key?('paths')
161
157
  # is there a source list option ?
162
158
  file_list = @opt_mgr.get_option(:sources)
163
159
  case file_list
@@ -165,32 +161,32 @@ module Aspera
165
161
  Log.log.debug('getting file list as parameters')
166
162
  # get remaining arguments
167
163
  file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
168
- raise CliBadArgument, 'specify at least one file on command line or use '\
164
+ raise Cli::BadArgument, 'specify at least one file on command line or use ' \
169
165
  "--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
170
166
  when FILE_LIST_FROM_TRANSFER_SPEC
171
167
  Log.log.debug('assume list provided in transfer spec')
172
168
  special_case_direct_with_list =
173
- @opt_mgr.get_option(:transfer, is_type: :mandatory).eql?(:direct) &&
174
- Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline, @opt_mgr.get_option(:transfer_info))
175
- raise CliBadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
169
+ @opt_mgr.get_option(:transfer, mandatory: true).eql?(:direct) &&
170
+ Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_command_line, @opt_mgr.get_option(:transfer_info))
171
+ raise Cli::BadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
176
172
  # here we assume check of sources is made in transfer agent
177
173
  return @transfer_paths
178
174
  when Array
179
175
  Log.log.debug('getting file list as extended value')
180
- raise CliBadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
176
+ raise Cli::BadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
181
177
  else
182
- raise CliBadArgument, "sources must be a Array, not #{file_list.class}"
178
+ raise Cli::BadArgument, "sources must be a Array, not #{file_list.class}"
183
179
  end
184
180
  # here, file_list is an Array or String
185
181
  if !@transfer_paths.nil?
186
182
  Log.log.warn('--sources overrides paths from --ts')
187
183
  end
188
- case @opt_mgr.get_option(:src_type, is_type: :mandatory)
184
+ case @opt_mgr.get_option(:src_type, mandatory: true)
189
185
  when :list
190
186
  # when providing a list, just specify source
191
187
  @transfer_paths = file_list.map{|i|{'source' => i}}
192
188
  when :pair
193
- raise CliBadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
189
+ raise Cli::BadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
194
190
  @transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
195
191
  else raise 'Unsupported src_type'
196
192
  end
@@ -208,40 +204,37 @@ module Aspera
208
204
  case transfer_spec['direction']
209
205
  when Fasp::TransferSpec::DIRECTION_RECEIVE
210
206
  # init default if required in any case
211
- @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
207
+ @transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
212
208
  when Fasp::TransferSpec::DIRECTION_SEND
213
209
  if transfer_spec.dig('tags', Fasp::TransferSpec::TAG_RESERVED, 'node', 'access_key')
214
210
  # gen4
215
- @transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.key?('destination_root_id')
211
+ @transfer_spec_command_line.delete('destination_root') if @transfer_spec_command_line.key?('destination_root_id')
216
212
  elsif transfer_spec.key?('token')
217
213
  # gen3
218
214
  # in that case, destination is set in return by application (API/upload_setup)
219
215
  # but to_folder was used in initial API call
220
- @transfer_spec_cmdline.delete('destination_root')
216
+ @transfer_spec_command_line.delete('destination_root')
221
217
  else
222
218
  # init default if required
223
- @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
219
+ @transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
224
220
  end
225
221
  end
226
222
  # update command line paths, unless destination already has one
227
- @transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
228
- transfer_spec.merge!(@transfer_spec_cmdline)
229
- # remove values that are nil (user wants to delete)
230
- transfer_spec.delete_if { |_key, value| value.nil? }
223
+ @transfer_spec_command_line['paths'] = transfer_spec['paths'] || ts_source_paths
224
+ # updated transfer spec with command line
225
+ updated_ts(transfer_spec)
231
226
  # create transfer agent
232
227
  set_agent_by_options
233
228
  Log.log.debug{"transfer agent is a #{@agent.class}"}
234
229
  @agent.start_transfer(transfer_spec, token_regenerator: rest_token)
235
- # list of : :success or error message
236
- result = @agent.wait_for_transfers_completion
237
- @progress_listener.reset
238
- Fasp::AgentBase.validate_status_list(result)
230
+ # list of: :success or "error message string"
231
+ result = @agent.wait_for_completion
239
232
  send_email_transfer_notification(transfer_spec, result)
240
233
  return result
241
234
  end
242
235
 
243
236
  def send_email_transfer_notification(transfer_spec, statuses)
244
- return if @opt_mgr.get_option(:notif_to).nil?
237
+ return if @opt_mgr.get_option(:notify_to).nil?
245
238
  global_status = self.class.session_status(statuses)
246
239
  email_vars = {
247
240
  global_transfer_status: global_status,
@@ -249,7 +242,7 @@ module Aspera
249
242
  body: "Transfer is: #{global_status}",
250
243
  ts: transfer_spec
251
244
  }
252
- @config.send_email_template(email_template_default: DEFAULT_TRANSFER_NOTIF_TMPL, values: email_vars)
245
+ @config.send_email_template(email_template_default: DEFAULT_TRANSFER_NOTIFY_TEMPLATE, values: email_vars)
253
246
  end
254
247
 
255
248
  # shut down if agent requires it
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/log'
4
+ require 'ruby-progressbar'
5
+
6
+ module Aspera
7
+ module Cli
8
+ # progress bar for transfers, supports multi-session
9
+ class TransferProgress
10
+ def initialize
11
+ reset
12
+ end
13
+
14
+ def reset
15
+ @progress_bar = nil
16
+ # key is session id
17
+ @sessions = {}
18
+ @completed = false
19
+ @title = ''
20
+ end
21
+
22
+ def total(key)
23
+ @sessions.values.inject(0){|m, s|m + s[key]}
24
+ end
25
+
26
+ def event(session_id:, type:, info: nil)
27
+ Log.log.debug{"progress: #{type} #{session_id} #{info}"}
28
+ if session_id.nil? && !type.eql?(:pre_start)
29
+ raise 'Internal error: session_id is nil'
30
+ end
31
+ return if @completed
32
+ if @progress_bar.nil?
33
+ @progress_bar = ProgressBar.create(
34
+ format: '%t %a %B %p%% %r Mbps %E',
35
+ rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
36
+ title: '',
37
+ total: nil)
38
+ end
39
+ need_increment = true
40
+ case type
41
+ when :pre_start
42
+ @title = info
43
+ when :session_start
44
+ raise "Session #{session_id} already started" if @sessions[session_id]
45
+ @sessions[session_id] = {
46
+ job_size: 0, # total size of transfer (pre-calc)
47
+ current: 0
48
+ }
49
+ @title = ''
50
+ when :session_size
51
+ @sessions[session_id][:job_size] = info.to_i
52
+ current_total = total(:job_size)
53
+ @progress_bar.total = current_total unless current_total.eql?(@progress_bar.total) || current_total < @progress_bar.progress
54
+ when :transfer
55
+ if !@progress_bar.total.nil?
56
+ need_increment = false
57
+ @sessions[session_id][:current] = info.to_i
58
+ current_total = total(:current)
59
+ @progress_bar.progress = current_total unless @progress_bar.progress.eql?(current_total)
60
+ end
61
+ when :end
62
+ @title = ''
63
+ @completed = true
64
+ @progress_bar.finish
65
+ else
66
+ raise "Unknown event type #{type}"
67
+ end
68
+ new_title = @sessions.length < 2 ? @title : "[#{@sessions.length}] #{@title}"
69
+ @progress_bar.title = new_title unless @progress_bar.title.eql?(new_title)
70
+ @progress_bar.increment if need_increment && !@completed
71
+ end
72
+ end
73
+ end
74
+ end
@@ -4,6 +4,6 @@ module Aspera
4
4
  module Cli
5
5
  # for beta add extension : .beta1
6
6
  # for dev version add extension : .pre
7
- VERSION = '4.13.0'
7
+ VERSION = '4.15.0'
8
8
  end
9
9
  end
data/lib/aspera/colors.rb CHANGED
@@ -1,21 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:words
4
+
3
5
  # simple vt100 colors
4
6
  class String
5
7
  class << self
6
8
  private
7
9
 
8
- def vtcmd(code); "\e[#{code}m"; end
10
+ def vt_cmd(code); "\e[#{code}m"; end
9
11
  end
10
12
  # see https://en.wikipedia.org/wiki/ANSI_escape_code
11
13
  # symbol is the method name added to String
12
14
  # it adds control chars to set color (and reset at the end).
13
- VTSTYLES = {
15
+ VT_STYLES = {
14
16
  bold: 1,
17
+ dim: 2,
15
18
  italic: 3,
16
19
  underline: 4,
17
20
  blink: 5,
18
21
  reverse_color: 7,
22
+ invisible: 8,
19
23
  black: 30,
20
24
  red: 31,
21
25
  green: 32,
@@ -33,17 +37,17 @@ class String
33
37
  bg_cyan: 46,
34
38
  bg_gray: 47
35
39
  }.freeze
36
- private_constant :VTSTYLES
37
- # defines methods to String, one per entry in VTSTYLES
38
- VTSTYLES.each do |name, code|
39
- if $stderr.tty?
40
- begin_seq = vtcmd(code)
40
+ private_constant :VT_STYLES
41
+ # defines methods to String, one per entry in VT_STYLES
42
+ VT_STYLES.each do |name, code|
43
+ if $stdout.tty?
44
+ begin_seq = vt_cmd(code)
41
45
  end_code = 0 # by default reset all
42
46
  if code <= 7 then code + 20
43
47
  elsif code <= 37 then 39
44
48
  elsif code <= 47 then 49
45
49
  end
46
- end_seq = vtcmd(end_code)
50
+ end_seq = vt_cmd(end_code)
47
51
  define_method(name){"#{begin_seq}#{self}#{end_seq}"}
48
52
  else
49
53
  define_method(name){self}
@@ -55,31 +55,34 @@ module Aspera
55
55
 
56
56
  attr_reader :params_definition
57
57
 
58
- # @param param_hash
58
+ # @param [Hash] param_hash with parameters
59
+ # @param [Hash] params_definition with definition of parameters
59
60
  def initialize(param_hash, params_definition)
60
61
  @param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
61
62
  @params_definition = params_definition
62
- @result_env = {}
63
- @result_args = []
63
+ @result = {
64
+ env: {},
65
+ args: []
66
+ }
64
67
  @used_param_names = []
65
68
  end
66
69
 
67
- # adds keys :env :args with resulting values after processing
68
- # warns if some parameters were not used
69
- def add_env_args(env, args)
70
- Log.log.debug{"ENV=#{@result_env}, ARGS=#{@result_args}"}
70
+ # add processed parameters to env and args, warns about unused parameters
71
+ # @param [Hash] env_args with :env and :args
72
+ def add_env_args(env_args)
73
+ Log.log.debug{"add_env_args: ENV=#{@result[:env]}, ARGS=#{@result[:args]}"}
71
74
  # warn about non translated arguments
72
75
  @param_hash.each_pair{|key, val|Log.log.warn{"unrecognized parameter: #{key} = \"#{val}\""} if !@used_param_names.include?(key)}
73
76
  # set result
74
- env.merge!(@result_env)
75
- args.push(*@result_args)
77
+ env_args[:env].merge!(@result[:env])
78
+ env_args[:args].push(*@result[:args])
76
79
  return nil
77
80
  end
78
81
 
79
82
  # add options directly to command line
80
83
  def add_command_line_options(options)
81
84
  return if options.nil?
82
- options.each{|o|@result_args.push(o.to_s)}
85
+ options.each{|o|@result[:args].push(o.to_s)}
83
86
  end
84
87
 
85
88
  def process_params
@@ -157,7 +160,7 @@ module Aspera
157
160
  return
158
161
  when :envvar # set in env var
159
162
  raise 'error' unless options[:cli].key?(:variable)
160
- @result_env[options[:cli][:variable]] = parameter_value
163
+ @result[:env][options[:cli][:variable]] = parameter_value
161
164
  when :opt_without_arg # if present and true : just add option without value
162
165
  add_param = false
163
166
  case parameter_value
@@ -2,12 +2,13 @@
2
2
 
3
3
  require 'aspera/log'
4
4
  require 'aspera/rest'
5
+ require 'aspera/oauth'
5
6
  require 'xmlsimple'
6
7
 
7
8
  module Aspera
8
9
  class CosNode < Aspera::Node
9
10
  class << self
10
- def parameters_from_svc_creds(service_credentials, bucket_region)
11
+ def parameters_from_svc_credentials(service_credentials, bucket_region)
11
12
  # check necessary contents
12
13
  raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
13
14
  %w[apikey resource_instance_id endpoints].each do |field|
@@ -85,7 +86,7 @@ module Aspera
85
86
  receiver_client_ids: 'aspera_ats'
86
87
  }})
87
88
  # get delegated token to be placed in rest call header and in transfer tags
88
- @storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.get_authorization.gsub(/^Bearer /, '')
89
+ @storage_credentials['token'][TOKEN_FIELD] = Oauth.bearer_extract(delegated_oauth.get_authorization)
89
90
  @params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@storage_credentials)}
90
91
  end
91
92
  end
data/lib/aspera/data/6 CHANGED
Binary file
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:ignore USERPROFILE HOMEDRIVE HOMEPATH LC_CTYPE msys aarch
3
4
  require 'aspera/log'
4
5
  require 'rbconfig'
5
6
 
7
+ # cspell:words MEBI mswin bccwin
8
+
6
9
  module Aspera
7
10
  # detect OS, architecture, and specific stuff
8
11
  class Environment
@@ -50,7 +53,7 @@ module Aspera
50
53
  return CPU_PPC64
51
54
  when /s390/
52
55
  return CPU_S390
53
- when /arm/
56
+ when /arm/, /aarch64/
54
57
  # arm on mac has rosetta 2
55
58
  return CPU_X86_64 if os.eql?(OS_X)
56
59
  end
@@ -69,9 +72,9 @@ module Aspera
69
72
  # on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH%
70
73
  # so, tell Ruby the right way
71
74
  def fix_home
72
- return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV['USERPROFILE'])
73
- ENV['HOME'] = ENV['USERPROFILE']
74
- Log.log.debug{"Windows: set home to USERPROFILE: #{ENV['HOME']}"}
75
+ return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV.fetch('USERPROFILE', nil))
76
+ ENV['HOME'] = ENV.fetch('USERPROFILE', nil)
77
+ Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"}
75
78
  end
76
79
 
77
80
  def empty_binding
@@ -79,17 +82,19 @@ module Aspera
79
82
  end
80
83
 
81
84
  # secure execution of Ruby code
82
- def secure_eval(code)
83
- Kernel.send('lave'.reverse, code, empty_binding, __FILE__, __LINE__)
85
+ def secure_eval(code, file, line)
86
+ Kernel.send('lave'.reverse, code, empty_binding, file, line)
84
87
  end
85
88
 
86
89
  # value is provided in block
87
- def write_file_restricted(path, force: false)
90
+ def write_file_restricted(path, force: false, mode: nil)
88
91
  raise 'coding error, missing content block' unless block_given?
89
92
  if force || !File.exist?(path)
90
- File.unlink(path) rescue nil # Windows may give error
93
+ # Windows may give error
94
+ File.unlink(path) rescue nil
95
+ # content provided by block
91
96
  File.write(path, yield)
92
- restrict_file_access(path)
97
+ restrict_file_access(path, mode: mode)
93
98
  end
94
99
  return path
95
100
  end
@@ -109,6 +114,16 @@ module Aspera
109
114
  rescue => e
110
115
  Log.log.warn(e.message)
111
116
  end
117
+
118
+ def terminal?
119
+ $stdout.tty?
120
+ end
121
+
122
+ # @return true if we can display Unicode characters
123
+ def use_unicode?
124
+ @use_unicode = terminal? && ENV.values_at('LC_ALL', 'LC_CTYPE', 'LANG').compact.first.include?('UTF-8') if @use_unicode.nil?
125
+ return @use_unicode
126
+ end
112
127
  end # self
113
128
  end # Environment
114
129
  end # Aspera