aspera-cli 4.25.0.pre2 → 4.25.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 (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -0
  4. data/CONTRIBUTING.md +60 -18
  5. data/README.md +164 -133
  6. data/lib/aspera/agent/factory.rb +9 -6
  7. data/lib/aspera/agent/transferd.rb +4 -4
  8. data/lib/aspera/api/aoc.rb +33 -24
  9. data/lib/aspera/api/ats.rb +1 -0
  10. data/lib/aspera/api/faspex.rb +11 -5
  11. data/lib/aspera/ascmd.rb +1 -1
  12. data/lib/aspera/ascp/installation.rb +5 -5
  13. data/lib/aspera/cli/formatter.rb +15 -62
  14. data/lib/aspera/cli/manager.rb +8 -42
  15. data/lib/aspera/cli/plugins/aoc.rb +48 -30
  16. data/lib/aspera/cli/plugins/ats.rb +30 -36
  17. data/lib/aspera/cli/plugins/base.rb +6 -6
  18. data/lib/aspera/cli/plugins/config.rb +5 -4
  19. data/lib/aspera/cli/plugins/faspex.rb +5 -3
  20. data/lib/aspera/cli/plugins/faspex5.rb +10 -8
  21. data/lib/aspera/cli/plugins/faspio.rb +3 -1
  22. data/lib/aspera/cli/plugins/node.rb +9 -6
  23. data/lib/aspera/cli/plugins/oauth.rb +12 -11
  24. data/lib/aspera/cli/plugins/preview.rb +2 -2
  25. data/lib/aspera/cli/transfer_agent.rb +1 -2
  26. data/lib/aspera/cli/version.rb +1 -1
  27. data/lib/aspera/command_line_builder.rb +5 -5
  28. data/lib/aspera/dot_container.rb +108 -0
  29. data/lib/aspera/id_generator.rb +7 -10
  30. data/lib/aspera/oauth/base.rb +25 -38
  31. data/lib/aspera/oauth/factory.rb +5 -6
  32. data/lib/aspera/oauth/generic.rb +1 -1
  33. data/lib/aspera/oauth/jwt.rb +1 -1
  34. data/lib/aspera/oauth/url_json.rb +4 -3
  35. data/lib/aspera/oauth/web.rb +2 -2
  36. data/lib/aspera/preview/file_types.rb +1 -1
  37. data/lib/aspera/rest.rb +5 -2
  38. data/lib/aspera/ssh.rb +6 -5
  39. data/lib/aspera/sync/conf.schema.yaml +2 -2
  40. data/lib/aspera/transfer/parameters.rb +6 -6
  41. data/lib/aspera/transfer/spec.schema.yaml +3 -3
  42. data/lib/aspera/transfer/spec_doc.rb +11 -21
  43. data/lib/aspera/uri_reader.rb +17 -3
  44. data.tar.gz.sig +0 -0
  45. metadata +2 -1
  46. metadata.gz.sig +0 -0
@@ -14,50 +14,38 @@ module Aspera
14
14
  # Bearer Token Usage: https://tools.ietf.org/html/rfc6750
15
15
  class Base
16
16
  Aspera.require_method!(:create_token)
17
- # @param ** Parameters for REST
18
- # @param client_id [String, nil]
19
- # @param client_secret [String, nil]
20
- # @param scope [String, nil]
21
- # @param use_query [bool] Provide parameters in query instead of body
22
- # @param path_token [String] API end point to create a token from base URL
23
- # @param token_field [String] Field in result that contains the token
24
- # @param cache_ids [Array, nil] List of unique identifiers for cache id generation
17
+ # @param params [Hash] Parameters for token creation (client_id, client_secret, scope, etc...)
18
+ # @param use_query [Boolean] Provide parameters in query instead of body
19
+ # @param path_token [String] API end point to create a token from base URL
20
+ # @param token_field [String] Field in result that contains the token
21
+ # @param cache_ids [Array] List of unique identifiers for cache id generation
22
+ # @param **rest_params [Hash] Parameters for REST
25
23
  def initialize(
26
- client_id: nil,
27
- client_secret: nil,
28
- scope: nil,
24
+ params: {},
29
25
  use_query: false,
30
26
  path_token: 'token',
31
27
  token_field: Factory::TOKEN_FIELD,
32
- cache_ids: nil,
28
+ cache_ids: [],
33
29
  **rest_params
34
30
  )
31
+ Aspera.assert_type(params, Hash)
32
+ Aspera.assert_type(cache_ids, Array)
35
33
  # This is the OAuth API
36
34
  @api = Rest.new(**rest_params)
37
- @scope = nil
38
- @token_cache_id = nil
35
+ @params = params.dup.freeze
39
36
  @path_token = path_token
40
37
  @token_field = token_field
41
- @client_id = client_id
42
- @client_secret = client_secret
43
38
  @use_query = use_query
44
- @base_cache_ids = cache_ids.nil? ? [] : cache_ids.clone
45
- Aspera.assert_type(@base_cache_ids, Array)
46
- # TODO: this shall be done in class, using cache_ids
47
- @base_cache_ids.push(@api.auth_params[:username]) if @api.auth_params.key?(:username)
48
- @base_cache_ids.compact!
49
- @base_cache_ids.freeze
50
- self.scope = scope
39
+ # TODO: :username and :scope shall be done in class, using cache_ids
40
+ @token_cache_id = Factory.cache_id(@api.base_url, self.class, cache_ids, rest_params[:username], @params[:scope])
51
41
  end
52
42
 
53
- # Scope can be modified after creation, then update identifier for cache
54
- def scope=(scope)
55
- @scope = scope
56
- # generate token unique identifier for persistency (memory/disk cache)
57
- @token_cache_id = Factory.cache_id(@api.base_url, self.class, @base_cache_ids, @scope)
58
- end
59
-
60
- attr_reader :scope, :api, :path_token, :client_id
43
+ # The OAuth API Object
44
+ attr_reader :api
45
+ # Sub path to generate token
46
+ attr_reader :path_token
47
+ # Parameters to generate token
48
+ attr_reader :params
61
49
 
62
50
  # Helper method to create token as per RFC
63
51
  # @return [HTTPResponse]
@@ -68,17 +56,16 @@ module Aspera
68
56
  return @api.create(@path_token, creation_params, content_type: Rest::MIME_WWW, ret: :resp)
69
57
  end
70
58
 
59
+ # Create base parameters for token creation calls
71
60
  # @param add_secret [Boolean] Add secret in default call parameters
72
61
  # @return [Hash] Optional general parameters
73
- def optional_scope_client_id(add_secret: false)
74
- call_params = {}
75
- call_params[:scope] = @scope unless @scope.nil?
76
- call_params[:client_id] = @client_id unless @client_id.nil?
77
- call_params[:client_secret] = @client_secret unless !add_secret || @client_id.nil? || @client_secret.nil?
62
+ def base_params(add_secret: false)
63
+ call_params = @params.dup
64
+ call_params.delete(:client_secret) unless add_secret
78
65
  return call_params
79
66
  end
80
67
 
81
- # @return value suitable for Authorization header
68
+ # @return [String] value suitable for Authorization header
82
69
  def authorization(**kwargs)
83
70
  return OAuth::Factory.bearer_authorization(token(**kwargs))
84
71
  end
@@ -114,7 +101,7 @@ module Aspera
114
101
  if !refresh_token.nil?
115
102
  Log.log.info{"refresh token=[#{refresh_token}]".bg_green}
116
103
  begin
117
- http = create_token_call(optional_scope_client_id(add_secret: true).merge(grant_type: 'refresh_token', refresh_token: refresh_token))
104
+ http = create_token_call(base_params(add_secret: true).merge(grant_type: 'refresh_token', refresh_token: refresh_token))
118
105
  # Save only if success
119
106
  json_data = http.body
120
107
  token_data = JSON.parse(json_data)
@@ -36,14 +36,13 @@ module Aspera
36
36
  return authorization[SPACE_BEARER_AUTH_SCHEME.length..-1]
37
37
  end
38
38
 
39
+ # Generate a unique cache id for a token creator
40
+ # @param url [String] Base URL of the OAuth server
41
+ # @param creator_class [Class] Class of the token creator
42
+ # @param params [Array] List of parameters (can be nested) to uniquely identify the token
39
43
  # @return a unique cache identifier
40
44
  def cache_id(url, creator_class, *params)
41
- return IdGenerator.from_list([
42
- PERSIST_CATEGORY_TOKEN,
43
- url,
44
- Factory.class_to_id(creator_class)
45
- ] +
46
- params)
45
+ return IdGenerator.from_list(PERSIST_CATEGORY_TOKEN, url, Factory.class_to_id(creator_class), params)
47
46
  end
48
47
 
49
48
  # @return snake version of class name
@@ -23,7 +23,7 @@ module Aspera
23
23
  end
24
24
 
25
25
  def create_token
26
- return create_token_call(optional_scope_client_id.merge(@create_params))
26
+ return create_token_call(base_params.merge(@create_params))
27
27
  end
28
28
  end
29
29
  Factory.instance.register_token_creator(Generic)
@@ -61,7 +61,7 @@ module Aspera
61
61
  Log.log.debug{"private=[#{@private_key_obj}]"}
62
62
  assertion = JWT.encode(jwt_payload, @private_key_obj, 'RS256', @headers)
63
63
  Log.log.debug{"assertion=[#{assertion}]"}
64
- return create_token_call(optional_scope_client_id.merge(grant_type: GRANT_TYPE, assertion: assertion))
64
+ return create_token_call(base_params.merge(grant_type: GRANT_TYPE, assertion: assertion))
65
65
  end
66
66
  end
67
67
  Factory.instance.register_token_creator(Jwt)
@@ -6,8 +6,9 @@ module Aspera
6
6
  module OAuth
7
7
  # This class is used to create a token using a JSON body and a URL
8
8
  class UrlJson < Base
9
- # @param url URL to send the JSON body
10
- # @param json JSON body to send
9
+ # @param url [Hash] Query parameters to send
10
+ # @param json [Hash] Body parameters to send as JSON
11
+ # @param generic_params [Hash] Generic parameters for OAuth::Base
11
12
  def initialize(
12
13
  url:,
13
14
  json:,
@@ -22,7 +23,7 @@ module Aspera
22
23
  api.call(
23
24
  operation: 'POST',
24
25
  subpath: path_token,
25
- query: @query.merge(scope: scope), # scope is here because it may change over time (node)
26
+ query: @query.merge(scope: params[:scope]), # scope is here because it may change over time (node)
26
27
  content_type: Rest::MIME_JSON,
27
28
  body: @body,
28
29
  headers: {'Accept' => Rest::MIME_JSON},
@@ -32,7 +32,7 @@ module Aspera
32
32
  random_state = SecureRandom.uuid
33
33
  login_page_url = Rest.build_uri(
34
34
  "#{api.base_url}/#{@path_authorize}",
35
- optional_scope_client_id.merge(response_type: 'code', redirect_uri: @redirect_uri, state: random_state)
35
+ base_params.merge(response_type: 'code', redirect_uri: @redirect_uri, state: random_state)
36
36
  )
37
37
  # here, we need a human to authorize on a web page
38
38
  Log.log.info{"login_page_url=#{login_page_url}".bg_red.gray}
@@ -44,7 +44,7 @@ module Aspera
44
44
  received_params = web_server.received_request
45
45
  Aspera.assert(random_state.eql?(received_params['state'])){'wrong received state'}
46
46
  # exchange code for token
47
- return create_token_call(optional_scope_client_id(add_secret: true).merge(
47
+ return create_token_call(base_params(add_secret: true).merge(
48
48
  grant_type: 'authorization_code',
49
49
  code: received_params['code'],
50
50
  redirect_uri: @redirect_uri
@@ -53,7 +53,7 @@ module Aspera
53
53
 
54
54
  private_constant :SUPPORTED_MIME_TYPES
55
55
 
56
- # @attr use_mimemagic [bool] true to use mimemagic to determine real mime type based on file content
56
+ # @attr use_mimemagic [Boolean] `true` to use mimemagic to determine real mime type based on file content
57
57
  attr_accessor :use_mimemagic
58
58
 
59
59
  def initialize
data/lib/aspera/rest.rb CHANGED
@@ -307,9 +307,12 @@ module Aspera
307
307
  # @param body [Hash, String] body parameters
308
308
  # @param headers [Hash] additional headers (override Content-Type)
309
309
  # @param save_to_file [String, nil](filepath)
310
- # @param exception [Bool] `true`, error raise exception
310
+ # @param exception [Boolean] `true`, error raise exception
311
311
  # @param ret [:data, :resp, :both] Tell to return only data, only http response, or both
312
- # @return [Object, Array] only data, only http response, or both
312
+ # @return [(HTTPResponse,Hash)] If ret is :both
313
+ # @return [HTTPResponse] If ret is :resp
314
+ # @return [Hash] If ret is :data
315
+ # @raise [RestCallError] on error if `exception` is true
313
316
  def call(
314
317
  operation:,
315
318
  subpath: nil,
data/lib/aspera/ssh.rb CHANGED
@@ -35,16 +35,17 @@ module Aspera
35
35
  # ssh_options: same as Net::SSH.start
36
36
  # see: https://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
37
37
  def initialize(host, username, ssh_options)
38
- Log.log.debug{"ssh:#{username}@#{host}"}
39
- Log.log.debug{"ssh_options:#{ssh_options}"}
40
38
  Aspera.assert_type(host, String)
41
39
  Aspera.assert_type(username, String)
42
40
  Aspera.assert_type(ssh_options, Hash)
43
- ssh_options[:use_agent] = false unless ssh_options.key?(:use_agent)
44
41
  @host = host
45
42
  @username = username
46
- @ssh_options = ssh_options
47
- @ssh_options[:logger] = Log.log
43
+ @ssh_options = ssh_options.dup
44
+ @ssh_options[:logger] = Log.log unless @ssh_options.key?(:logger)
45
+ @ssh_options[:verbose] = :warn unless @ssh_options.key?(:verbose)
46
+ @ssh_options[:use_agent] = false unless @ssh_options.key?(:use_agent)
47
+ Log.log.debug{"ssh:#{@username}@#{@host}"}
48
+ Log.dump(:ssh_options, @ssh_options)
48
49
  end
49
50
 
50
51
  # Anything on stderr raises an exception
@@ -4,8 +4,8 @@ description: Async session description, identical to `asyncs` Node API.
4
4
  $comment: >-
5
5
  This is a YAML version of the original JSON schema from asyncs API.
6
6
  It is modified with special `x-` fields.
7
- x-ts-name [Bool,String] `true` if same name in transfer spec, else real name in transfer spec
8
- x-ts-convert [String] Name of methods to convert value from transfer spec to `conf`
7
+ x-ts-name [Boolean,String] `true` if same name in transfer spec, else real name in transfer spec
8
+ x-ts-convert [String] Name of methods to convert value from transfer spec to `conf`
9
9
  `x-` fields are also documented in `command_line_builder.rb`
10
10
  type: object
11
11
  required:
@@ -45,12 +45,12 @@ module Aspera
45
45
  end
46
46
  end
47
47
 
48
- # @param job_spec [Hash] Transfer spec
49
- # @param ascp_args [Array] Other ascp args
50
- # @param quiet [Bool] Remove ascp output
51
- # @param trusted_certs [Array] Trusted certificates
52
- # @param client_ssh_key [Symbol] :rsa or :dsa
53
- # @param check_ignore_cb [Proc] Callback
48
+ # @param job_spec [Hash] Transfer spec
49
+ # @param ascp_args [Array] Other ascp args
50
+ # @param quiet [Boolean] Remove ascp output
51
+ # @param trusted_certs [Array] Trusted certificates
52
+ # @param client_ssh_key [:rsa,:dsa] :rsa or :dsa
53
+ # @param check_ignore_cb [Proc] Callback
54
54
  def initialize(
55
55
  job_spec,
56
56
  ascp_args: nil,
@@ -7,8 +7,8 @@ $comment: >-
7
7
  The following extensions are used for mapping to ascp arguments.
8
8
  x-cli-envvar [String] Name of env var
9
9
  x-cli-option [String] Command line option (starts with "-"), or `true`: same as ts, or `false`: not an option
10
- x-cli-switch [Bool] `true` if option has no arg, else by default option has a value
11
- x-cli-special [Bool] `true` if not anaged by command line generator (special handling: option or argument)
10
+ x-cli-switch [Boolean] `true` if option has no arg, else by default option has a value
11
+ x-cli-special [Boolean] `true` if not anaged by command line generator (special handling: option or argument)
12
12
  x-cli-convert [String,Hash] Method name for Convert object or Conversion Hash for enum: ts to arg
13
13
  x-agents [Array] Supported agents (for doc only), if not specified: all
14
14
  x-deprecation [String] Deprecation message for doc
@@ -106,7 +106,7 @@ properties:
106
106
  delete_source:
107
107
  $comment: "TODO: implement"
108
108
  description: >-
109
- Remove transfered source files after transfer success.
109
+ Remove transferred source files after transfer success.
110
110
  Equivalent to `remove_after_transfer` + `remove_empty_directories` + `remove_empty_source_directory`.
111
111
  Take precedence over those.
112
112
  type: boolean
@@ -8,20 +8,14 @@ module Aspera
8
8
  # Generate documentation from Schema, for Transfer Spec, or async Conf spec
9
9
  class SpecDoc
10
10
  class << self
11
- # First letter of agent name symbol
12
- def agent_to_short(agent_sym)
13
- agent_sym.to_sym.eql?(:direct) ? :a : agent_sym.to_s[0].to_sym
14
- end
15
-
16
- # @param formatter [Cli::Formatter] Formatter to use, methods: markdown, tick, check_row
11
+ # @param formatter [Cli::Formatter] Formatter to use, methods: markdown_text, tick, check_row
17
12
  # @param include_option [Boolean] `true` : include CLI options
18
13
  # @param agent_columns [Boolean] `true` : include agents columns
19
14
  # @param schema [Hash] The JSON spec
20
15
  # @return [Array] a table suitable to display in manual
21
16
  def man_table(formatter, include_option: false, agent_columns: true, schema: Spec::SCHEMA)
22
- col_local = agent_to_short(:direct)
23
17
  cols = %i[name type description]
24
- cols.insert(-2, *AGENT_LIST.map(&:last)) if agent_columns
18
+ cols.insert(-2, *Agent::Factory::ALL.values.map{ |i| i[:short]}.sort) if agent_columns
25
19
  rows = []
26
20
  schema['properties'].each do |name, info|
27
21
  rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info).last.map{ |h| h.merge(name: "#{name}.#{h[:name]}")}) if info['type'].eql?('object') && info['properties']
@@ -35,25 +29,25 @@ module Aspera
35
29
  # Render Markdown formatting and split lines
36
30
  columns[:description] =
37
31
  info['description']
38
- .gsub(Markdown::FORMATS){formatter.markdown(Regexp.last_match)}
32
+ .gsub(Markdown::FORMATS){formatter.markdown_text(Regexp.last_match)}
39
33
  .split("\n") if info.key?('description')
40
34
  columns[:description].unshift("DEPRECATED: #{info['x-deprecation']}") if info.key?('x-deprecation')
41
35
  # Add flags for supported agents in doc
42
36
  agents = []
43
- AGENT_LIST.each do |agent_info|
44
- agents.push(agent_info.last) if info['x-agents'].nil? || info['x-agents'].include?(agent_info.first.to_s)
37
+ Agent::Factory::ALL.each_key do |sym|
38
+ agents.push(sym) if info['x-agents'].nil? || info['x-agents'].include?(sym.to_s)
45
39
  end
46
- Aspera.assert(agents.include?(col_local)){"#{name}: x-cli-option requires agent direct (or nil)"} if info['x-cli-option']
40
+ Aspera.assert(agents.include?(:direct)){"#{name}: x-cli-option requires agent direct (or nil)"} if info['x-cli-option']
47
41
  if agent_columns
48
- AGENT_LIST.each do |agent_info|
49
- columns[agent_info.last] = formatter.tick(agents.include?(agent_info.last))
42
+ Agent::Factory::ALL.each do |sym, names|
43
+ columns[names[:short]] = formatter.tick(agents.include?(sym))
50
44
  end
51
45
  else
52
- columns[:description].push("(#{agents.map(&:upcase).join(', ')})") unless agents.length.eql?(AGENT_LIST.length)
46
+ columns[:description].push("(#{agents.map{ |i| Agent::Factory::ALL[i][:short].to_s.upcase}.sort.join(', ')})") unless agents.length.eql?(Agent::Factory::ALL.length)
53
47
  end
54
48
  # Only keep lines that are usable in supported agents
55
49
  next false if agents.empty?
56
- columns[:description].push("Allowed values: #{info['enum'].map{ |v| formatter.markdown("`#{v}`")}.join(', ')}") if info.key?('enum')
50
+ columns[:description].push("Allowed values: #{info['enum'].map{ |v| formatter.markdown_text("`#{v}`")}.join(', ')}") if info.key?('enum')
57
51
  if include_option
58
52
  envvar_prefix = ''
59
53
  cli_option =
@@ -70,17 +64,13 @@ module Aspera
70
64
  "#{info['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}"
71
65
  end
72
66
  short = info.key?('x-cli-short') ? "(#{info['x-cli-short']})" : nil
73
- columns[:description].push("(#{'special:' if info['x-cli-special']}#{envvar_prefix}#{formatter.markdown("`#{cli_option}`")})#{short}") if cli_option
67
+ columns[:description].push("(#{'special:' if info['x-cli-special']}#{envvar_prefix}#{formatter.markdown_text("`#{cli_option}`")})#{short}") if cli_option
74
68
  end
75
69
  rows.push(formatter.check_row(columns))
76
70
  end
77
71
  [cols, rows.sort_by{ |i| i[:name]}]
78
72
  end
79
73
  end
80
- # Agents shown in manual for parameters (sub list)
81
- AGENT_LIST = Agent::Factory.instance.list.map do |agent_sym|
82
- [agent_sym, agent_sym.to_s.capitalize, agent_to_short(agent_sym)]
83
- end.sort_by(&:last).freeze
84
74
  end
85
75
  end
86
76
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'uri'
4
+ require 'base64'
4
5
  require 'aspera/assert'
5
6
  require 'aspera/rest'
6
7
  require 'aspera/temp_file_manager'
@@ -19,6 +20,13 @@ module Aspera
19
20
  case uri.scheme
20
21
  when 'http', 'https'
21
22
  return Rest.new(base_url: uri_to_read, redirect_max: 5).read(nil, headers: {'Accept' => '*/*'})
23
+ when 'data'
24
+ metadata, encoded_data = uri.opaque.split(',', 2)
25
+ if metadata.end_with?(';base64')
26
+ Base64.decode64(encoded_data)
27
+ else
28
+ URI.decode_www_form_component(encoded_data)
29
+ end
22
30
  when SCHEME_FILE, NilClass
23
31
  local_file_path = uri.path
24
32
  raise Error, 'URL shall have a path, check syntax' if local_file_path.nil?
@@ -35,12 +43,18 @@ module Aspera
35
43
  # require specific file scheme: the path part is "relative", or absolute if there are 4 slash
36
44
  raise "use format: #{SCHEME_FILE_PFX2}<path>" unless url.start_with?(SCHEME_FILE_PFX2)
37
45
  return File.expand_path(url[SCHEME_FILE_PFX2.length..-1])
46
+ elsif url.start_with?('data:')
47
+ # download to temp file
48
+ # auto-delete on exit
49
+ temp_file = TempFileManager.instance.new_file_path_global('uri_reader')
50
+ File.write(temp_file, read(url), binmode: true)
51
+ return temp_file
38
52
  else
39
53
  # download to temp file
40
54
  # auto-delete on exit
41
- sdk_archive_path = TempFileManager.instance.new_file_path_global(suffix: File.basename(url))
42
- Aspera::Rest.new(base_url: url, redirect_max: 3).call(operation: 'GET', save_to_file: sdk_archive_path)
43
- return sdk_archive_path
55
+ temp_file = TempFileManager.instance.new_file_path_global(suffix: File.basename(url))
56
+ Aspera::Rest.new(base_url: url, redirect_max: 3).call(operation: 'GET', save_to_file: temp_file)
57
+ return temp_file
44
58
  end
45
59
  end
46
60
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.25.0.pre2
4
+ version: 4.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -367,6 +367,7 @@ files:
367
367
  - lib/aspera/data/5
368
368
  - lib/aspera/data/6
369
369
  - lib/aspera/data_repository.rb
370
+ - lib/aspera/dot_container.rb
370
371
  - lib/aspera/environment.rb
371
372
  - lib/aspera/faspex_gw.rb
372
373
  - lib/aspera/faspex_postproc.rb
metadata.gz.sig CHANGED
Binary file