fastlane 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/fastlane/lib/fastlane/actions/opt_out_usage.rb +6 -6
  3. data/fastlane/lib/fastlane/documentation/docs_generator.rb +2 -2
  4. data/fastlane/lib/fastlane/fastlane_require.rb +8 -1
  5. data/fastlane/lib/fastlane/version.rb +1 -1
  6. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +3 -2
  7. data/fastlane_core/lib/fastlane_core/ui/github_issue_inspector_reporter.rb +1 -3
  8. data/fastlane_core/lib/fastlane_core/update_checker/update_checker.rb +1 -1
  9. data/pilot/lib/pilot/commands_generator.rb +4 -3
  10. data/pilot/lib/pilot/tester_manager.rb +2 -6
  11. data/scan/lib/scan/detect_values.rb +15 -0
  12. data/scan/lib/scan/options.rb +21 -0
  13. data/scan/lib/scan/slack_poster.rb +3 -0
  14. data/scan/lib/scan/test_command_generator.rb +5 -0
  15. data/screengrab/lib/screengrab/runner.rb +6 -8
  16. data/snapshot/README.md +1 -1
  17. data/snapshot/lib/snapshot/fixes/README.md +1 -1
  18. data/snapshot/lib/snapshot/reports_generator.rb +1 -0
  19. data/spaceship/lib/spaceship/client.rb +52 -0
  20. data/spaceship/lib/spaceship/spaceauth_runner.rb +6 -1
  21. data/spaceship/lib/spaceship/tunes/app_version.rb +6 -0
  22. data/spaceship/lib/spaceship/tunes/tester.rb +1 -1
  23. data/spaceship/lib/spaceship/tunes/tunes_client.rb +100 -21
  24. metadata +3 -14
  25. data/fastlane/lib/.DS_Store +0 -0
  26. data/fastlane/lib/assets/.DS_Store +0 -0
  27. data/fastlane/lib/assets/completions/.DS_Store +0 -0
  28. data/fastlane/lib/fastlane/.DS_Store +0 -0
  29. data/fastlane/lib/fastlane/actions/.DS_Store +0 -0
  30. data/fastlane/lib/fastlane/actions/device_grid/.DS_Store +0 -0
  31. data/fastlane_core/lib/.DS_Store +0 -0
  32. data/fastlane_core/lib/fastlane_core/.DS_Store +0 -0
  33. data/snapshot/lib/.DS_Store +0 -0
  34. data/spaceship/lib/.DS_Store +0 -0
  35. data/spaceship/lib/spaceship/.DS_Store +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 727fe731430e8c06f7b4a0c66d044714c879ccaf
4
- data.tar.gz: c65677292ad39144080c5bdef78de0aea6b82961
3
+ metadata.gz: 4b1b63ff2fa7ae1f6bb83ee77cdd83530f36a7be
4
+ data.tar.gz: 4a03adb249cc48ad1eb0ce05a0857162d03ea8fd
5
5
  SHA512:
6
- metadata.gz: 3f90f8dbb9649502c8300d957e5535db5ebdcc6abe900489be9173b44be4c55b91fb39f210b8a58d048517d6115f55fc3c0e7c17187bb3097d55234d5a7f2388
7
- data.tar.gz: 73591c45604420668dddaf8ba46227fd6f44ab517e781bea5db8d1355e02c82a973680683740e55bbb5ad1088b24736e7a8d1aaf0c534283134bf0bae0b66c80
6
+ metadata.gz: 58cdcf0f0b64a3b56de09792a95b686fdcc4c9d6caa45cc16af7462de6f0f0f7605fe3323389c0ae6b97b87e6caa48d7dc57fcb9734c940e1dbc96a2c29b5e1b
7
+ data.tar.gz: d32509f435ad25c115a469686ee4a7faace5bbaa4313180f12d5908d2aa6185390b66522ce0330ba1abae9c0394d2722aaa77fa88eb931707600ab4bf015880e
@@ -12,11 +12,11 @@ module Fastlane
12
12
 
13
13
  def self.details
14
14
  [
15
- "By default, fastlane will share the used actions. ",
16
- "No personal information is shard. More information available on ",
17
- "https://github.com/fastlane/enhancer\n",
18
- "Using this action you can opt out"
19
- ].join('')
15
+ "By default, fastlane will track what actions are being used",
16
+ "No personal information is shared. More information available on",
17
+ "https://github.com/fastlane/enhancer",
18
+ "Add `opt_out_usage` at the top of your Fastfile"
19
+ ].join(' ')
20
20
  end
21
21
 
22
22
  def self.author
@@ -29,7 +29,7 @@ module Fastlane
29
29
 
30
30
  def self.example_code
31
31
  [
32
- 'opt_out_usage'
32
+ 'opt_out_usage # add this to the top of your Fastfile'
33
33
  ]
34
34
  end
35
35
 
@@ -36,8 +36,8 @@ module Fastlane
36
36
  end
37
37
 
38
38
  output << "This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run."
39
- output << "More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools)."
40
- output << "The documentation of fastlane can be found on [https://docs.fastlane.tools](https://docs.fastlane.tools)."
39
+ output << "More information about fastlane can be found on [fastlane.tools](https://fastlane.tools)."
40
+ output << "The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools)."
41
41
  output << ""
42
42
 
43
43
  File.write(output_path, output.join("\n"))
@@ -2,7 +2,7 @@ module Fastlane
2
2
  class FastlaneRequire
3
3
  class << self
4
4
  def install_gem_if_needed(gem_name: nil, require_gem: true)
5
- gem_require_name = gem_name.tr("-", "/") # from "fastlane-plugin-xcversion" to "fastlane/plugin/xcversion"
5
+ gem_require_name = format_gem_require_name(gem_name)
6
6
 
7
7
  # check if it's installed
8
8
  if gem_installed?(gem_name)
@@ -62,6 +62,13 @@ module Fastlane
62
62
 
63
63
  return gems.first
64
64
  end
65
+
66
+ def format_gem_require_name(gem_name)
67
+ # from "fastlane-plugin-xcversion" to "fastlane/plugin/xcversion"
68
+ gem_name = gem_name.tr("-", "/") if gem_name.start_with? "fastlane-plugin-"
69
+
70
+ return gem_name
71
+ end
65
72
  end
66
73
  end
67
74
  end
@@ -1,4 +1,4 @@
1
1
  module Fastlane
2
- VERSION = '2.11.0'.freeze
2
+ VERSION = '2.12.0'.freeze
3
3
  DESCRIPTION = "The easiest way to automate beta deployments and releases for your iOS and Android apps".freeze
4
4
  end
@@ -140,16 +140,17 @@ module Commander
140
140
  # use a bit of Ruby duck-typing to check whether the unknown exception type implements the right
141
141
  # method. If so, we'll present any returned error info in the manner of a user_error!
142
142
  error_info = e.respond_to?(:preferred_error_info) ? e.preferred_error_info : nil
143
+ should_show_github_issues = e.respond_to?(:show_github_issues) ? e.show_github_issues : true
143
144
 
144
145
  if error_info
145
146
  error_info = error_info.join("\n\t") if error_info.kind_of?(Array)
146
147
 
147
- show_github_issues(error_info)
148
+ show_github_issues(error_info) if should_show_github_issues
148
149
 
149
150
  display_user_error!(e, error_info)
150
151
  else
151
152
  # Pass the error instead of a message so that the inspector can do extra work to simplify the query
152
- show_github_issues(e)
153
+ show_github_issues(e) if should_show_github_issues
153
154
 
154
155
  # From https://stackoverflow.com/a/4789702/445598
155
156
  # We do this to make the actual error message red and therefore more visible
@@ -29,7 +29,6 @@ module Fastlane
29
29
  puts "Found no similar issues. To create a new issue, please visit:"
30
30
  puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/issues/new"
31
31
  puts "Run `fastlane env` to append the fastlane environment to your issue"
32
- print_open_link_hint(true)
33
32
  end
34
33
 
35
34
  # Called when there have been networking issues in creating the report.
@@ -37,7 +36,6 @@ module Fastlane
37
36
  puts "Could not access the GitHub API, you may have better luck via the website."
38
37
  puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/search?q=#{query}&type=Issues&utf8=✓"
39
38
  puts "Error: #{error.name}"
40
- print_open_link_hint(true)
41
39
  end
42
40
 
43
41
  private
@@ -54,7 +52,7 @@ module Fastlane
54
52
 
55
53
  def print_open_link_hint(newline = false)
56
54
  puts "" if newline
57
- puts "🔗 You can ⌘ + double-click on links to open them directly in your browser." if Helper.mac?
55
+ puts "🔗 You can ⌘ + double-click on links to open them directly in your browser." if Helper.mac?
58
56
  end
59
57
  end
60
58
  end
@@ -79,7 +79,7 @@ module FastlaneCore
79
79
  def self.update_command(gem_name: "fastlane")
80
80
  if Helper.bundler?
81
81
  "bundle update #{gem_name.downcase}"
82
- elsif Helper.contained_fastlane?
82
+ elsif Helper.contained_fastlane? || Helper.homebrew?
83
83
  "fastlane update_fastlane"
84
84
  else
85
85
  "sudo gem update #{gem_name.downcase}"
@@ -31,11 +31,12 @@ module Pilot
31
31
  begin
32
32
  mgr.public_send(action, config)
33
33
  rescue => ex
34
- failures.push(address)
35
- UI.message("[#{address}]: #{ex}")
34
+ message = "[#{address}]: #{ex}"
35
+ failures << message
36
+ UI.error(message)
36
37
  end
37
38
  end
38
- UI.user_error!("Some operations failed: #{failures}") unless failures.empty?
39
+ UI.user_error!("Some operations failed: #{failures.join(', ')}") unless failures.empty?
39
40
  end
40
41
 
41
42
  def run
@@ -118,12 +118,8 @@ module Pilot
118
118
  begin
119
119
  int_testers = Spaceship::Tunes::Tester::Internal.all
120
120
  ext_testers = Spaceship::Tunes::Tester::External.all
121
- rescue => ex
122
- if ex.to_s.include?("Forbidden")
123
- UI.user_error!("You don't have the permission to list the testers of your whole team. Please provide an app identifier to list all testers of a specific application.")
124
- else
125
- raise ex
126
- end
121
+ rescue Spaceship::Client::InsufficientPermissions
122
+ UI.user_error!("You don't have the permission to list the testers of your whole team. Please provide an app identifier to list all testers of a specific application.")
127
123
  end
128
124
 
129
125
  list_global(int_testers, "Internal Testers")
@@ -36,9 +36,24 @@ module Scan
36
36
 
37
37
  default_derived_data
38
38
 
39
+ coerce_to_array_of_strings(:only_testing)
40
+ coerce_to_array_of_strings(:skip_testing)
41
+
39
42
  return config
40
43
  end
41
44
 
45
+ def self.coerce_to_array_of_strings(config_key)
46
+ config_value = Scan.config[config_key]
47
+
48
+ return if config_value.nil?
49
+
50
+ # splitting on comma allows us to support comma-separated lists of values
51
+ # from the command line, even though the ConfigItem is not defined as an
52
+ # Array type
53
+ config_value = config_value.split(',') unless config_value.kind_of?(Array)
54
+ Scan.config[config_key] = config_value.map(&:to_s)
55
+ end
56
+
42
57
  def self.default_derived_data
43
58
  return unless Scan.config[:derived_data_path].to_s.empty?
44
59
  default_path = Scan.project.build_settings(key: "BUILT_PRODUCTS_DIR")
@@ -3,6 +3,11 @@ require "credentials_manager"
3
3
 
4
4
  module Scan
5
5
  class Options
6
+ def self.verify_type(item_name, acceptable_types, value)
7
+ type_ok = [Array, String].any? { |type| value.kind_of?(type) }
8
+ UI.user_error!("'#{item_name}' should be of type #{acceptable_types.join(' or ')} but found: #{value.class.name}") unless type_ok
9
+ end
10
+
6
11
  def self.available_options
7
12
  containing = Helper.fastlane_enabled? ? './fastlane' : '.'
8
13
 
@@ -166,6 +171,22 @@ module Scan
166
171
  verify_block: proc do |value|
167
172
  UI.user_error!("File not found at path '#{File.expand_path(value)}'") unless File.exist?(value)
168
173
  end),
174
+ FastlaneCore::ConfigItem.new(key: :only_testing,
175
+ env_name: "SCAN_ONLY_TESTING",
176
+ description: "Array of strings matching Test Bundle/Test Suite/Test Cases to run",
177
+ optional: true,
178
+ is_string: false,
179
+ verify_block: proc do |value|
180
+ verify_type('only_testing', [Array, String], value)
181
+ end),
182
+ FastlaneCore::ConfigItem.new(key: :skip_testing,
183
+ env_name: "SCAN_SKIP_TESTING",
184
+ description: "Array of strings matching Test Bundle/Test Suite/Test Cases to skip",
185
+ optional: true,
186
+ is_string: false,
187
+ verify_block: proc do |value|
188
+ verify_type('skip_testing', [Array, String], value)
189
+ end),
169
190
  FastlaneCore::ConfigItem.new(key: :slack_url,
170
191
  short_option: "-i",
171
192
  env_name: "SLACK_URL",
@@ -51,7 +51,10 @@ module Scan
51
51
 
52
52
  if result.code.to_i == 200
53
53
  UI.success('Successfully sent Slack notification')
54
+ elsif result.code.to_i == 404
55
+ UI.error("The Slack URL you provided could not be reached (404)")
54
56
  else
57
+ UI.error("The Slack notification could not be sent:")
55
58
  UI.error(result.to_s)
56
59
  end
57
60
  end
@@ -41,6 +41,11 @@ module Scan
41
41
  options << "-xcconfig '#{config[:xcconfig]}'" if config[:xcconfig]
42
42
  options << config[:xcargs] if config[:xcargs]
43
43
 
44
+ # detect_values will ensure that these values are present as Arrays if
45
+ # they are present at all
46
+ options += config[:only_testing].map { |test_id| "-only-testing:#{test_id}" } if config[:only_testing]
47
+ options += config[:skip_testing].map { |test_id| "-skip-testing:#{test_id}" } if config[:skip_testing]
48
+
44
49
  options
45
50
  end
46
51
 
@@ -187,16 +187,14 @@ module Screengrab
187
187
 
188
188
  def uninstall_apks(device_serial, app_package_name, tests_package_name)
189
189
  UI.message 'Uninstalling app APK'
190
- apk_uninstall_output = run_adb_command("adb -s #{device_serial} uninstall #{app_package_name}",
191
- print_all: true,
192
- print_command: true)
193
- UI.user_error! "App APK could not be uninstalled" if apk_uninstall_output.include?("Failure [")
190
+ run_adb_command("adb -s #{device_serial} uninstall #{app_package_name}",
191
+ print_all: true,
192
+ print_command: true)
194
193
 
195
194
  UI.message 'Uninstalling tests APK'
196
- apk_uninstall_output = run_adb_command("adb -s #{device_serial} uninstall -r #{tests_package_name}",
197
- print_all: true,
198
- print_command: true)
199
- UI.user_error! "Tests APK could not be uninstalled" if apk_uninstall_output.include?("Failure [")
195
+ run_adb_command("adb -s #{device_serial} uninstall #{tests_package_name}",
196
+ print_all: true,
197
+ print_command: true)
200
198
  end
201
199
 
202
200
  def grant_permissions(device_serial)
@@ -334,7 +334,7 @@ _snapshot_ finds all these entries using a regex. The number of _snapshot_ outpu
334
334
 
335
335
  If you find a better way to do any of this, please submit an issue on GitHub or even a pull request :+1:
336
336
 
337
- Also, feel free to duplicate radar [23062925](https://openradar.appspot.com/radar?id=5056366381105152).
337
+ Radar [23062925](https://openradar.appspot.com/radar?id=5056366381105152) has been resolved with Xcode 8.3, so it's now possible to actually take screenshots from the simulator. We'll keep using the old approach for now, since many of you still want to use older versions of Xcode.
338
338
 
339
339
  # Tips
340
340
 
@@ -1,5 +1,5 @@
1
1
  ### Fixes
2
2
 
3
3
  This directory contains all the files we don't actually want to have, but are necessary due to open radars, e.g.
4
- - [rdar://23062925](https://openradar.appspot.com/radar?id=5056366381105152): Trigger screenshots from UI Tests
4
+
5
5
  - [rdar://23323159](https://openradar.appspot.com/radar?id=6127019184095232): Screenshots are broken when simulator is not scaled at 100%
@@ -53,6 +53,7 @@ module Snapshot
53
53
  'iPhone6' => "iPhone6 (4.7-Inch)",
54
54
  'iPhone5' => "iPhone5 (4-Inch)",
55
55
  'iPhone4' => "iPhone4 (3.5-Inch)",
56
+ 'iPhone SE' => "iPhone SE",
56
57
  'iPad2' => "iPad2",
57
58
  'iPadAir2' => 'iPad Air 2',
58
59
  'iPadPro(12.9-inch)' => 'iPad Air Pro (12.9 inch)',
@@ -50,6 +50,21 @@ module Spaceship
50
50
  # Raised when no user credentials were passed at all
51
51
  class NoUserCredentialsError < BasicPreferredInfoError; end
52
52
 
53
+ # User doesn't have enough permission for given action
54
+ class InsufficientPermissions < BasicPreferredInfoError
55
+ TITLE = 'Insufficient permissions for your Apple ID:'.freeze
56
+
57
+ def preferred_error_info
58
+ message ? [TITLE, message] : nil
59
+ end
60
+
61
+ # We don't want to show similar GitHub issues, as the error message
62
+ # should be pretty clear
63
+ def show_github_issues
64
+ false
65
+ end
66
+ end
67
+
53
68
  class UnexpectedResponse < StandardError
54
69
  attr_reader :error_info
55
70
 
@@ -426,13 +441,50 @@ module Spaceship
426
441
  end
427
442
 
428
443
  if content.nil?
444
+ # Check if the failure is due to missing permissions (iTunes Connect)
445
+ if response.body && response.body["messages"] && response.body["messages"]["error"].include?("Forbidden")
446
+ raise_insuffient_permission_error!
447
+ end
448
+
429
449
  raise UnexpectedResponse, response.body
450
+ elsif content.kind_of?(Hash) && (content["resultString"] || "").include?("NotAllowed")
451
+ # example content when doing a Developer Portal action with not enough permission
452
+ # => {"responseId"=>"e5013d83-c5cb-4ba0-bb62-734a8d56007f",
453
+ # "resultCode"=>1200,
454
+ # "resultString"=>"webservice.certificate.downloadNotAllowed",
455
+ # "userString"=>"You are not permitted to download this certificate.",
456
+ # "creationTimestamp"=>"2017-01-26T22:44:13Z",
457
+ # "protocolVersion"=>"QH65B2",
458
+ # "userLocale"=>"en_US",
459
+ # "requestUrl"=>"https://developer.apple.com/services-account/QH65B2/account/ios/certificate/downloadCertificateContent.action",
460
+ # "httpCode"=>200}
461
+ raise_insuffient_permission_error!(additional_error_string: content["userString"])
430
462
  else
431
463
  store_csrf_tokens(response)
432
464
  content
433
465
  end
434
466
  end
435
467
 
468
+ # This also gets called from subclasses
469
+ def raise_insuffient_permission_error!(additional_error_string: nil)
470
+ # get the method name of the request that failed
471
+ # `block in` is used very often for requests when surrounded for paging or retrying blocks
472
+ # The ! is part of some methods when they modify or delete a resource, so we don't want to show it
473
+ # Using `sub` instead of `delete` as we don't want to allow multiple matches
474
+ calling_method_name = caller_locations(2, 2).first.label.sub("block in", "").delete("!").strip
475
+ begin
476
+ team_id = "(Team ID #{self.team_id}) "
477
+ rescue
478
+ # Showing the team ID is something that's nice to have, however it might cause an exception
479
+ # when the user doesn't have any permission at all (e.g. failing at login)
480
+ # we still want the error message to show the actual string, but without the team_id in that case
481
+ team_id = ""
482
+ end
483
+ error_message = "User #{self.user} #{team_id}doesn't have enough permission for the following action: #{calling_method_name}"
484
+ error_message += " (#{additional_error_string})" if additional_error_string.to_s.length > 0
485
+ raise InsufficientPermissions, error_message
486
+ end
487
+
436
488
  private
437
489
 
438
490
  def directory_accessible?(path)
@@ -33,7 +33,12 @@ module Spaceship
33
33
  # Example:
34
34
  # name: DES5c148586daa451e55afb017aa62418f91
35
35
  # value: HSARMTKNSRVTWFlaF/ek8asaa9lymMA0dN8JQ6pY7B3F5kdqTxJvMT19EVEFX8EQudB/uNwBHOHzaa30KYTU/eCP/UF7vGTgxs6PAnlVWKscWssOVHfP2IKWUPaa4Dn+I6ilA7eAFQsiaaVT
36
- cookies = YAML.safe_load(itc_cookie_content)
36
+ cookies = YAML.safe_load(
37
+ itc_cookie_content,
38
+ [HTTP::Cookie, Time], # classes whitelist
39
+ [], # symbols whitelist
40
+ true # allow YAML aliases
41
+ )
37
42
 
38
43
  # We remove all the un-needed cookies
39
44
  cookies.select! do |cookie|
@@ -45,6 +45,11 @@ module Spaceship
45
45
  # @return (Bool) Should the app automatically be released once it's approved?
46
46
  attr_accessor :release_on_approval
47
47
 
48
+ # @return (Fixnum) Milliseconds for releasing in GMT (e.g. 1480435200000 = Tue, 29 Nov 2016 16:00:00 GMT).
49
+ # Use nil to unset. Setting this will supercede the release_on_approval field, so this field must be nil
50
+ # for release_on_approval to be used.
51
+ attr_accessor :auto_release_date
52
+
48
53
  # @return (Bool)
49
54
  attr_accessor :can_beta_test
50
55
 
@@ -135,6 +140,7 @@ module Spaceship
135
140
  'largeAppIcon.value.originalFileName' => :app_icon_original_name,
136
141
  'largeAppIcon.value.url' => :app_icon_url,
137
142
  'releaseOnApproval.value' => :release_on_approval,
143
+ 'autoReleaseDate.value' => :auto_release_date,
138
144
  'status' => :raw_status,
139
145
  'preReleaseBuild.buildVersion' => :build_version,
140
146
  'supportsAppleWatch' => :supports_apple_watch,
@@ -104,7 +104,7 @@ module Spaceship
104
104
  # @param last_name (String) (optional): The last name of the new tester
105
105
  # @param groups (Array) (option): Names/IDs of existing groups for the new tester
106
106
  # @example
107
- # Spaceship::Tunes::Tester.external.create!(email: "tester@mathiascarignani.com", first_name: "Cary", last_name:"Bennett", groups:["Testers"])
107
+ # Spaceship::Tunes::Tester.external.create!(email: "tester@mathiascarignani.com", first_name: "Cary", last_name: "Bennett", groups: ["Testers"])
108
108
  # @return (Tester): The newly created tester
109
109
  def create!(email: nil, first_name: nil, last_name: nil, groups: nil)
110
110
  data = client.create_tester!(tester: self,
@@ -46,9 +46,7 @@ module Spaceship
46
46
 
47
47
  # @return (Array) A list of all available teams
48
48
  def teams
49
- return @teams if @teams
50
- r = request(:get, "ra/user/detail")
51
- @teams = parse_response(r, 'data')['associatedAccounts'].sort_by do |team|
49
+ user_details_data['associatedAccounts'].sort_by do |team|
52
50
  [
53
51
  team['contentProvider']['name'],
54
52
  team['contentProvider']['contentProviderId']
@@ -214,10 +212,13 @@ module Spaceship
214
212
  errors << different_error if different_error
215
213
 
216
214
  if errors.count > 0 # they are separated by `.` by default
215
+ # Sample `error` content: [["Forbidden"]]
217
216
  if errors.count == 1 and errors.first == "You haven't made any changes."
218
217
  # This is a special error which we really don't care about
219
218
  elsif errors.count == 1 and errors.first.include?("try again later")
220
219
  raise ITunesConnectTemporaryError.new, errors.first
220
+ elsif errors.count == 1 and errors.first.include?("Forbidden")
221
+ raise_insuffient_permission_error!
221
222
  else
222
223
  raise ITunesConnectError.new, errors.join(' ')
223
224
  end
@@ -596,14 +597,61 @@ module Spaceship
596
597
  Spaceship::Tunes::AppVersionRef.factory(data)
597
598
  end
598
599
 
600
+ # Fetch the general information of the user, is used by various methods across spaceship
601
+ # Sample return value
602
+ # => {"associatedAccounts"=>
603
+ # [{"contentProvider"=>{"contentProviderId"=>11142800, "name"=>"Felix Krause", "contentProviderTypes"=>["Purple Software"]}, "roles"=>["Developer"], "lastLogin"=>1468784113000}],
604
+ # "sessionToken"=>{"dsId"=>"8501011116", "contentProviderId"=>18111111, "expirationDate"=>nil, "ipAddress"=>nil},
605
+ # "permittedActivities"=>
606
+ # {"EDIT"=>
607
+ # ["UserManagementSelf",
608
+ # "GameCenterTestData",
609
+ # "AppAddonCreation"],
610
+ # "REPORT"=>
611
+ # ["UserManagementSelf",
612
+ # "AppAddonCreation"],
613
+ # "VIEW"=>
614
+ # ["TestFlightAppExternalTesterManagement",
615
+ # ...
616
+ # "HelpGeneral",
617
+ # "HelpApplicationLoader"]},
618
+ # "preferredCurrencyCode"=>"EUR",
619
+ # "preferredCountryCode"=>nil,
620
+ # "countryOfOrigin"=>"AT",
621
+ # "isLocaleNameReversed"=>false,
622
+ # "feldsparToken"=>nil,
623
+ # "feldsparChannelName"=>nil,
624
+ # "hasPendingFeldsparBindingRequest"=>false,
625
+ # "isLegalUser"=>false,
626
+ # "userId"=>"1771111155",
627
+ # "firstname"=>"Detlef",
628
+ # "lastname"=>"Mueller",
629
+ # "isEmailInvalid"=>false,
630
+ # "hasContractInfo"=>false,
631
+ # "canEditITCUsersAndRoles"=>false,
632
+ # "canViewITCUsersAndRoles"=>true,
633
+ # "canEditIAPUsersAndRoles"=>false,
634
+ # "transporterEnabled"=>false,
635
+ # "contentProviderFeatures"=>["APP_SILOING", "PROMO_CODE_REDESIGN", ...],
636
+ # "contentProviderType"=>"Purple Software",
637
+ # "displayName"=>"Detlef",
638
+ # "contentProviderId"=>"18742800",
639
+ # "userFeatures"=>[],
640
+ # "visibility"=>true,
641
+ # "DYCVisibility"=>false,
642
+ # "contentProvider"=>"Felix Krause",
643
+ # "userName"=>"detlef@krausefx.com"}
644
+ def user_details_data
645
+ return @_cached_user_details if @_cached_user_details
646
+ r = request(:get, '/WebObjects/iTunesConnect.woa/ra/user/detail')
647
+ @_cached_user_details = parse_response(r, 'data')
648
+ end
649
+
599
650
  # Fetches the User Detail information from ITC. This gets called often and almost never changes
600
651
  # so we cache it
601
652
  # @return [UserDetail] the response
602
653
  def user_detail_data
603
- return @cached if @cached
604
- r = request(:get, '/WebObjects/iTunesConnect.woa/ra/user/detail')
605
- data = parse_response(r, 'data')
606
- @cached = Spaceship::Tunes::UserDetail.factory(data)
654
+ @_cached_user_detail_data ||= Spaceship::Tunes::UserDetail.factory(user_details_data)
607
655
  end
608
656
 
609
657
  #####################################################
@@ -631,7 +679,13 @@ module Spaceship
631
679
  # Build trains fail randomly very often
632
680
  # we need to catch those errors and retry
633
681
  # https://github.com/fastlane/fastlane/issues/6419
634
- if ex.to_s.include?("ITC.response.error.OPERATION_FAILED")
682
+ retry_error_messages = [
683
+ "ITC.response.error.OPERATION_FAILED",
684
+ "Internal Server Error",
685
+ "Service Unavailable"
686
+ ].freeze
687
+
688
+ if retry_error_messages.any? { |message| ex.to_s.include?(message) }
635
689
  tries -= 1
636
690
  if tries > 0
637
691
  logger.warn("Received temporary server error from iTunes Connect. Retrying the request...")
@@ -870,6 +924,10 @@ module Spaceship
870
924
  parse_response(r, 'data')['users']
871
925
  end
872
926
 
927
+ # Returns a list of available testing groups
928
+ # e.g.
929
+ # {"b6f65dbd-c845-4d91-bc39-0b661d608970" => "Boarding",
930
+ # "70402368-9deb-409f-9a26-bb3f215dfee3" => "Automatic"}
873
931
  def groups
874
932
  return @cached_groups if @cached_groups
875
933
  r = request(:get, '/WebObjects/iTunesConnect.woa/ra/users/pre/ext')
@@ -881,21 +939,42 @@ module Spaceship
881
939
  raise "Action not provided for this tester type." unless url
882
940
 
883
941
  tester_data = {
884
- emailAddress: {
885
- value: email
886
- },
887
- firstName: {
888
- value: first_name || ""
889
- },
890
- lastName: {
891
- value: last_name || ""
892
- },
893
- testing: {
894
- value: true
942
+ emailAddress: {
943
+ value: email
944
+ },
945
+ firstName: {
946
+ value: first_name || ""
947
+ },
948
+ lastName: {
949
+ value: last_name || ""
950
+ },
951
+ testing: {
952
+ value: true
953
+ }
954
+ }
955
+
956
+ if groups
957
+ tester_data[:groups] = groups.map do |group_name_or_group_id|
958
+ if self.groups.value?(group_name_or_group_id)
959
+ # This is an existing group, let's use that, the user specified the group name
960
+ group_name = group_name_or_group_id
961
+ group_id = self.groups.key(group_name_or_group_id)
962
+ elsif self.groups.key?(group_name_or_group_id)
963
+ # This is an existing group, let's use that, the user specified the group ID
964
+ group_name = self.groups[group_name_or_group_id]
965
+ group_id = group_name_or_group_id
966
+ else
967
+ group_name = group_name_or_group_id
968
+ group_id = nil # this is expected by the iTC API
969
+ end
970
+
971
+ {
972
+ "id" => group_id,
973
+ "name" => {
974
+ "value" => group_name
895
975
  }
896
976
  }
897
- if groups
898
- tester_data[:groups] = groups.map { |x| { "id" => x } }
977
+ end
899
978
  end
900
979
 
901
980
  data = { testers: [tester_data] }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.0
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2017-01-25 00:00:00.000000000 Z
17
+ date: 2017-01-27 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: slack-notifier
@@ -739,15 +739,12 @@ files:
739
739
  - deliver/lib/deliver/upload_price_tier.rb
740
740
  - deliver/lib/deliver/upload_screenshots.rb
741
741
  - fastlane/README.md
742
- - fastlane/lib/.DS_Store
743
- - fastlane/lib/assets/.DS_Store
744
742
  - fastlane/lib/assets/Actions.md.erb
745
743
  - fastlane/lib/assets/AppfileTemplate
746
744
  - fastlane/lib/assets/AppfileTemplateAndroid
747
745
  - fastlane/lib/assets/AvailablePlugins.md.erb
748
746
  - fastlane/lib/assets/DefaultFastfileTemplate
749
747
  - fastlane/lib/assets/FastfileTemplateAndroid
750
- - fastlane/lib/assets/completions/.DS_Store
751
748
  - fastlane/lib/assets/completions/completion.bash
752
749
  - fastlane/lib/assets/completions/completion.sh
753
750
  - fastlane/lib/assets/completions/completion.zsh
@@ -758,10 +755,8 @@ files:
758
755
  - fastlane/lib/assets/s3_plist_template.erb
759
756
  - fastlane/lib/assets/s3_version_template.erb
760
757
  - fastlane/lib/fastlane.rb
761
- - fastlane/lib/fastlane/.DS_Store
762
758
  - fastlane/lib/fastlane/action.rb
763
759
  - fastlane/lib/fastlane/action_collector.rb
764
- - fastlane/lib/fastlane/actions/.DS_Store
765
760
  - fastlane/lib/fastlane/actions/README.md
766
761
  - fastlane/lib/fastlane/actions/actions_helper.rb
767
762
  - fastlane/lib/fastlane/actions/adb.rb
@@ -802,7 +797,6 @@ files:
802
797
  - fastlane/lib/fastlane/actions/delete_keychain.rb
803
798
  - fastlane/lib/fastlane/actions/deliver.rb
804
799
  - fastlane/lib/fastlane/actions/deploygate.rb
805
- - fastlane/lib/fastlane/actions/device_grid/.DS_Store
806
800
  - fastlane/lib/fastlane/actions/device_grid/README.md
807
801
  - fastlane/lib/fastlane/actions/dotgpg_environment.rb
808
802
  - fastlane/lib/fastlane/actions/download.rb
@@ -1013,10 +1007,8 @@ files:
1013
1007
  - fastlane/lib/fastlane/tools.rb
1014
1008
  - fastlane/lib/fastlane/version.rb
1015
1009
  - fastlane_core/README.md
1016
- - fastlane_core/lib/.DS_Store
1017
1010
  - fastlane_core/lib/assets/XMLTemplate.xml.erb
1018
1011
  - fastlane_core/lib/fastlane_core.rb
1019
- - fastlane_core/lib/fastlane_core/.DS_Store
1020
1012
  - fastlane_core/lib/fastlane_core/cert_checker.rb
1021
1013
  - fastlane_core/lib/fastlane_core/command_executor.rb
1022
1014
  - fastlane_core/lib/fastlane_core/configuration/commander_generator.rb
@@ -1172,7 +1164,6 @@ files:
1172
1164
  - sigh/lib/sigh/resign.rb
1173
1165
  - sigh/lib/sigh/runner.rb
1174
1166
  - snapshot/README.md
1175
- - snapshot/lib/.DS_Store
1176
1167
  - snapshot/lib/assets/SnapfileTemplate
1177
1168
  - snapshot/lib/assets/SnapshotHelper.swift
1178
1169
  - snapshot/lib/assets/SnapshotHelper2-3.swift
@@ -1197,11 +1188,9 @@ files:
1197
1188
  - snapshot/lib/snapshot/test_command_generator.rb
1198
1189
  - snapshot/lib/snapshot/update.rb
1199
1190
  - spaceship/README.md
1200
- - spaceship/lib/.DS_Store
1201
1191
  - spaceship/lib/assets/languageMapping.json
1202
1192
  - spaceship/lib/assets/languageMappingReadable.json
1203
1193
  - spaceship/lib/spaceship.rb
1204
- - spaceship/lib/spaceship/.DS_Store
1205
1194
  - spaceship/lib/spaceship/babosa_fix.rb
1206
1195
  - spaceship/lib/spaceship/base.rb
1207
1196
  - spaceship/lib/spaceship/client.rb
@@ -1305,7 +1294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1305
1294
  version: '0'
1306
1295
  requirements: []
1307
1296
  rubyforge_project:
1308
- rubygems_version: 2.6.6
1297
+ rubygems_version: 2.5.1
1309
1298
  signing_key:
1310
1299
  specification_version: 4
1311
1300
  summary: The easiest way to automate beta deployments and releases for your iOS and
Binary file
Binary file
Binary file
Binary file