fastlane 2.149.0 → 2.150.0.rc4

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +82 -82
  3. data/deliver/lib/deliver.rb +0 -1
  4. data/deliver/lib/deliver/app_screenshot.rb +26 -25
  5. data/deliver/lib/deliver/options.rb +6 -11
  6. data/deliver/lib/deliver/runner.rb +7 -21
  7. data/deliver/lib/deliver/setup.rb +5 -30
  8. data/deliver/lib/deliver/submit_for_review.rb +155 -90
  9. data/deliver/lib/deliver/upload_metadata.rb +355 -143
  10. data/deliver/lib/deliver/upload_price_tier.rb +22 -8
  11. data/deliver/lib/deliver/upload_screenshots.rb +134 -39
  12. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +1 -1
  13. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +77 -96
  14. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +3 -2
  15. data/fastlane/lib/fastlane/actions/download_dsyms.rb +7 -1
  16. data/fastlane/lib/fastlane/actions/google_play_track_release_names.rb +74 -0
  17. data/fastlane/lib/fastlane/actions/set_changelog.rb +23 -20
  18. data/fastlane/lib/fastlane/actions/slack.rb +1 -1
  19. data/fastlane/lib/fastlane/actions/spm.rb +7 -0
  20. data/fastlane/lib/fastlane/actions/upload_symbols_to_crashlytics.rb +1 -32
  21. data/fastlane/lib/fastlane/lane.rb +3 -3
  22. data/fastlane/lib/fastlane/swift_fastlane_function.rb +8 -4
  23. data/fastlane/lib/fastlane/version.rb +1 -1
  24. data/fastlane/swift/ControlCommand.swift +1 -0
  25. data/fastlane/swift/Deliverfile.swift +1 -1
  26. data/fastlane/swift/Fastlane.swift +48 -12
  27. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/xcshareddata/xcschemes/FastlaneRunner.xcscheme +3 -9
  28. data/fastlane/swift/Gymfile.swift +1 -1
  29. data/fastlane/swift/LaneFileProtocol.swift +2 -5
  30. data/fastlane/swift/Matchfile.swift +1 -1
  31. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  32. data/fastlane/swift/Precheckfile.swift +1 -1
  33. data/fastlane/swift/RubyCommand.swift +29 -6
  34. data/fastlane/swift/RubyCommandable.swift +1 -0
  35. data/fastlane/swift/Runner.swift +85 -13
  36. data/fastlane/swift/Scanfile.swift +1 -1
  37. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  38. data/fastlane/swift/Screengrabfile.swift +1 -1
  39. data/fastlane/swift/Snapshotfile.swift +1 -1
  40. data/fastlane/swift/SnapshotfileProtocol.swift +3 -3
  41. data/fastlane/swift/SocketClient.swift +76 -45
  42. data/fastlane/swift/SocketClientDelegateProtocol.swift +1 -1
  43. data/fastlane/swift/SocketResponse.swift +1 -0
  44. data/fastlane_core/lib/fastlane_core/configuration/config_item.rb +1 -3
  45. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +89 -52
  46. data/fastlane_core/lib/fastlane_core/pkg_file_analyser.rb +7 -0
  47. data/fastlane_core/lib/fastlane_core/project.rb +0 -1
  48. data/frameit/lib/frameit/device_types.rb +100 -100
  49. data/produce/lib/produce/itunes_connect.rb +32 -21
  50. data/produce/lib/produce/options.rb +3 -3
  51. data/sigh/lib/assets/resign.sh +7 -7
  52. data/snapshot/lib/assets/SnapshotHelper.swift +5 -5
  53. data/snapshot/lib/assets/SnapshotHelperXcode8.swift +3 -3
  54. data/snapshot/lib/snapshot/options.rb +0 -1
  55. data/snapshot/lib/snapshot/reports_generator.rb +8 -1
  56. data/spaceship/lib/spaceship/.DS_Store +0 -0
  57. data/spaceship/lib/spaceship/client.rb +4 -3
  58. data/spaceship/lib/spaceship/connect_api.rb +25 -2
  59. data/spaceship/lib/spaceship/connect_api/client.rb +97 -31
  60. data/spaceship/lib/spaceship/connect_api/file_uploader.rb +98 -0
  61. data/spaceship/lib/spaceship/connect_api/model.rb +1 -1
  62. data/spaceship/lib/spaceship/connect_api/models/age_rating_declaration.rb +113 -0
  63. data/spaceship/lib/spaceship/connect_api/models/app.rb +120 -3
  64. data/spaceship/lib/spaceship/connect_api/models/app_category.rb +94 -0
  65. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +67 -0
  66. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +38 -0
  67. data/spaceship/lib/spaceship/connect_api/models/app_preview.rb +129 -0
  68. data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +71 -0
  69. data/spaceship/lib/spaceship/connect_api/models/app_price.rb +22 -0
  70. data/spaceship/lib/spaceship/connect_api/models/app_price_tier.rb +12 -0
  71. data/spaceship/lib/spaceship/connect_api/models/app_review_attachment.rb +71 -0
  72. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +93 -0
  73. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +101 -0
  74. data/spaceship/lib/spaceship/connect_api/models/app_store_review_detail.rb +51 -0
  75. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +183 -0
  76. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +86 -0
  77. data/spaceship/lib/spaceship/connect_api/models/app_store_version_phased_release.rb +36 -0
  78. data/spaceship/lib/spaceship/connect_api/models/app_store_version_submission.rb +26 -0
  79. data/spaceship/lib/spaceship/connect_api/models/build.rb +4 -0
  80. data/spaceship/lib/spaceship/connect_api/models/idfa_declaration.rb +40 -0
  81. data/spaceship/lib/spaceship/connect_api/models/reset_ratings_request.rb +26 -0
  82. data/spaceship/lib/spaceship/connect_api/models/territory.rb +27 -0
  83. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +10 -3
  84. data/spaceship/lib/spaceship/connect_api/tunes/client.rb +33 -0
  85. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +841 -0
  86. data/spaceship/lib/spaceship/spaceauth_runner.rb +2 -2
  87. data/supply/lib/supply/client.rb +19 -0
  88. data/supply/lib/supply/reader.rb +16 -0
  89. metadata +44 -39
  90. data/deliver/lib/deliver/upload_assets.rb +0 -27
  91. data/fastlane/lib/assets/.s3_html_template.erb.swp +0 -0
  92. data/fastlane/lib/fastlane/actions/.hipchat.rb.swp +0 -0
  93. data/fastlane/lib/fastlane/actions/.slack.rb.swp +0 -0
  94. data/scan/lib/scan/.options.rb.swp +0 -0
@@ -54,7 +54,7 @@ module Produce
54
54
  optional: true,
55
55
  default_value: "ios",
56
56
  verify_block: proc do |value|
57
- UI.user_error!("The platform can only be ios or osx") unless %('ios', 'osx').include?(value)
57
+ UI.user_error!("The platform can only be ios or osx") unless %('ios', 'osx', 'tvos').include?(value)
58
58
  end),
59
59
  FastlaneCore::ConfigItem.new(key: :platforms,
60
60
  short_option: "-J",
@@ -64,13 +64,13 @@ module Produce
64
64
  optional: true,
65
65
  type: Array,
66
66
  verify_block: proc do |values|
67
- types = %w(ios osx)
67
+ types = %w(ios osx tvos)
68
68
  UI.user_error!("The platform can only be #{types}") unless (values - types).empty?
69
69
  end),
70
70
  FastlaneCore::ConfigItem.new(key: :language,
71
71
  short_option: "-m",
72
72
  env_name: "PRODUCE_LANGUAGE",
73
- description: "Primary Language (e.g. 'English', 'German')",
73
+ description: "Primary Language (e.g. 'en-US', 'fr-FR')",
74
74
  default_value: "English",
75
75
  verify_block: proc do |language|
76
76
  end),
@@ -620,7 +620,7 @@ function resign {
620
620
  # Start with using what comes in provisioning profile entitlements before patching
621
621
  cp -f "$PROFILE_ENTITLEMENTS" "$PATCHED_ENTITLEMENTS"
622
622
 
623
- log "Removing blacklisted keys from patched profile"
623
+ log "Removing denylisted keys from patched profile"
624
624
  # See https://github.com/facebook/buck/issues/798 and https://github.com/facebook/buck/pull/802/files
625
625
 
626
626
  # Update in https://github.com/facebook/buck/commit/99c0fbc3ab5ecf04d186913374f660683deccdef
@@ -628,7 +628,7 @@ function resign {
628
628
 
629
629
  # Buck changes referenced above are not self-explanatory and do not seem exhaustive or up-to-date
630
630
  # Comments below explain the rules applied to each key in order to make realignment with future Xcode export logic easier
631
- BLACKLISTED_KEYS=(\
631
+ DENYLISTED_KEYS=(\
632
632
  # PP list identifiers inconsistent with app-defined ones and this key does not seem to appear in IPA entitlements, so ignore it
633
633
  "com.apple.developer.icloud-container-development-container-identifiers" \
634
634
  # This key has an invalid generic value in PP (actual value is set by Xcode during export), see dedicated processing a few blocks below
@@ -637,7 +637,7 @@ function resign {
637
637
  "com.apple.developer.icloud-container-identifiers" \
638
638
  # PP enable all available services and not app-defined ones, must use App entitlements value
639
639
  "com.apple.developer.icloud-services" \
640
- # Was already blacklisted in previous version, but has someone ever seen this key in a PP?
640
+ # Was already denylisted in previous version, but has someone ever seen this key in a PP?
641
641
  "com.apple.developer.restricted-resource-mode" \
642
642
  # If actually used by the App, this value will be set in its entitlements
643
643
  "com.apple.developer.nfc.readersession.formats" \
@@ -675,15 +675,15 @@ function resign {
675
675
  "com.apple.developer.default-data-protection" \
676
676
  # PP seem to list the same groups as the App, but use App entitlements value to be sure
677
677
  "com.apple.security.application-groups" \
678
- # Was already blacklisted in previous version, seems to be an artifact from an old Xcode release
678
+ # Was already denylisted in previous version, seems to be an artifact from an old Xcode release
679
679
  "com.apple.developer.maps" \
680
680
  # If actually used by the App, this value will be set in its entitlements
681
681
  "com.apple.external-accessory.wireless-configuration"
682
682
  )
683
683
 
684
- # Blacklisted keys must not be included into new profile, so remove them from patched profile
685
- for KEY in "${BLACKLISTED_KEYS[@]}"; do
686
- log "Removing blacklisted key: $KEY"
684
+ # Denylisted keys must not be included into new profile, so remove them from patched profile
685
+ for KEY in "${DENYLISTED_KEYS[@]}"; do
686
+ log "Removing denylisted key: $KEY"
687
687
  PlistBuddy -c "Delete $KEY" "$PATCHED_ENTITLEMENTS" 2>/dev/null
688
688
  done
689
689
 
@@ -218,7 +218,7 @@ open class Snapshot: NSObject {
218
218
 
219
219
  private extension XCUIElementAttributes {
220
220
  var isNetworkLoadingIndicator: Bool {
221
- if hasWhiteListedIdentifier { return false }
221
+ if hasAllowListedIdentifier { return false }
222
222
 
223
223
  let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20)
224
224
  let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3)
@@ -226,10 +226,10 @@ private extension XCUIElementAttributes {
226
226
  return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize
227
227
  }
228
228
 
229
- var hasWhiteListedIdentifier: Bool {
230
- let whiteListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
229
+ var hasAllowListedIdentifier: Bool {
230
+ let allowListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
231
231
 
232
- return whiteListedIdentifiers.contains(identifier)
232
+ return allowListedIdentifiers.contains(identifier)
233
233
  }
234
234
 
235
235
  func isStatusBar(_ deviceWidth: CGFloat) -> Bool {
@@ -279,4 +279,4 @@ private extension CGFloat {
279
279
 
280
280
  // Please don't remove the lines below
281
281
  // They are used to detect outdated configuration files
282
- // SnapshotHelperVersion [1.22]
282
+ // SnapshotHelperVersion [1.23]
@@ -159,8 +159,8 @@ open class Snapshot: NSObject {
159
159
 
160
160
  extension XCUIElement {
161
161
  var isLoadingIndicator: Bool {
162
- let whiteListedLoaders = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
163
- if whiteListedLoaders.contains(self.identifier) {
162
+ let allowListedLoaders = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
163
+ if allowListedLoaders.contains(self.identifier) {
164
164
  return false
165
165
  }
166
166
  return self.frame.size == CGSize(width: 10, height: 20)
@@ -169,4 +169,4 @@ extension XCUIElement {
169
169
 
170
170
  // Please don't remove the lines below
171
171
  // They are used to detect outdated configuration files
172
- // SnapshotHelperXcode8Version [1.4]
172
+ // SnapshotHelperXcode8Version [1.5]
@@ -156,7 +156,6 @@ module Snapshot
156
156
  env_name: 'SNAPSHOT_HTML_TEMPLATE',
157
157
  short_option: "-e",
158
158
  description: "A path to screenshots.html template",
159
- default_value: File.join(Snapshot::ROOT, "lib", "snapshot/page.html.erb"),
160
159
  optional: true),
161
160
 
162
161
  # Everything around building
@@ -5,6 +5,14 @@ module Snapshot
5
5
  require 'erb'
6
6
  require 'fastimage'
7
7
 
8
+ def html_path
9
+ if Snapshot.config[:html_template]
10
+ Snapshot.config[:html_template]
11
+ else
12
+ File.join(Snapshot::ROOT, "lib", "snapshot/page.html.erb")
13
+ end
14
+ end
15
+
8
16
  def generate
9
17
  UI.message("Generating HTML Report")
10
18
 
@@ -36,7 +44,6 @@ module Snapshot
36
44
  end
37
45
  end
38
46
 
39
- html_path = Snapshot.config[:html_template]
40
47
  html = ERB.new(File.read(html_path)).result(binding) # https://web.archive.org/web/20160430190141/www.rrn.dk/rubys-erb-templating-system
41
48
 
42
49
  export_path = "#{screens_path}/screenshots.html"
@@ -194,15 +194,16 @@ module Spaceship
194
194
  self.new(cookie: another_client.instance_variable_get(:@cookie), current_team_id: another_client.team_id)
195
195
  end
196
196
 
197
- def initialize(cookie: nil, current_team_id: nil)
197
+ def initialize(cookie: nil, current_team_id: nil, timeout: nil)
198
198
  options = {
199
199
  request: {
200
- timeout: (ENV["SPACESHIP_TIMEOUT"] || 300).to_i,
201
- open_timeout: (ENV["SPACESHIP_TIMEOUT"] || 300).to_i
200
+ timeout: (ENV["SPACESHIP_TIMEOUT"] || timeout || 300).to_i,
201
+ open_timeout: (ENV["SPACESHIP_TIMEOUT"] || timeout || 300).to_i
202
202
  }
203
203
  }
204
204
  @current_team_id = current_team_id
205
205
  @cookie = cookie || HTTP::CookieJar.new
206
+
206
207
  @client = Faraday.new(self.class.hostname, options) do |c|
207
208
  c.response(:json, content_type: /\bjson$/)
208
209
  c.response(:xml, content_type: /\bxml$/)
@@ -1,10 +1,12 @@
1
1
  require 'spaceship/connect_api/model'
2
2
  require 'spaceship/connect_api/response'
3
3
  require 'spaceship/connect_api/token'
4
+ require 'spaceship/connect_api/file_uploader'
4
5
 
5
6
  require 'spaceship/connect_api/provisioning/provisioning'
6
7
  require 'spaceship/connect_api/testflight/testflight'
7
8
  require 'spaceship/connect_api/users/users'
9
+ require 'spaceship/connect_api/tunes/tunes'
8
10
 
9
11
  require 'spaceship/connect_api/models/bundle_id_capability'
10
12
  require 'spaceship/connect_api/models/bundle_id'
@@ -30,11 +32,32 @@ require 'spaceship/connect_api/models/build_delivery'
30
32
  require 'spaceship/connect_api/models/build_beta_detail'
31
33
  require 'spaceship/connect_api/models/pre_release_version'
32
34
 
35
+ require 'spaceship/connect_api/models/age_rating_declaration'
36
+ require 'spaceship/connect_api/models/app_category'
37
+ require 'spaceship/connect_api/models/app_info'
38
+ require 'spaceship/connect_api/models/app_info_localization'
39
+ require 'spaceship/connect_api/models/app_preview_set'
40
+ require 'spaceship/connect_api/models/app_preview'
41
+ require 'spaceship/connect_api/models/app_price'
42
+ require 'spaceship/connect_api/models/app_price_tier'
43
+ require 'spaceship/connect_api/models/app_review_attachment'
44
+ require 'spaceship/connect_api/models/app_store_review_detail'
45
+ require 'spaceship/connect_api/models/app_store_version_submission'
46
+ require 'spaceship/connect_api/models/app_screenshot_set'
47
+ require 'spaceship/connect_api/models/app_screenshot'
48
+ require 'spaceship/connect_api/models/app_store_version_localization'
49
+ require 'spaceship/connect_api/models/app_store_version_phased_release'
50
+ require 'spaceship/connect_api/models/app_store_version'
51
+ require 'spaceship/connect_api/models/idfa_declaration'
52
+ require 'spaceship/connect_api/models/reset_ratings_request'
53
+ require 'spaceship/connect_api/models/territory'
54
+
33
55
  module Spaceship
34
56
  class ConnectAPI
35
57
  extend Spaceship::ConnectAPI::Provisioning
36
58
  extend Spaceship::ConnectAPI::TestFlight
37
59
  extend Spaceship::ConnectAPI::Users
60
+ extend Spaceship::ConnectAPI::Tunes
38
61
 
39
62
  @token = nil
40
63
 
@@ -63,9 +86,9 @@ module Spaceship
63
86
 
64
87
  # Map from fastlane input and Spaceship::TestFlight platform values
65
88
  case platform.to_sym
66
- when :appletvos
89
+ when :appletvos, :tvos
67
90
  return Spaceship::ConnectAPI::Platform::TV_OS
68
- when :osx
91
+ when :osx, :macos, :mac
69
92
  return Spaceship::ConnectAPI::Platform::MAC_OS
70
93
  when :ios
71
94
  return Spaceship::ConnectAPI::Platform::IOS
@@ -13,7 +13,7 @@ module Spaceship
13
13
  # Instantiates a client with cookie session or a JWT token.
14
14
  def initialize(cookie: nil, current_team_id: nil, token: nil)
15
15
  if token.nil?
16
- super(cookie: cookie, current_team_id: current_team_id)
16
+ super(cookie: cookie, current_team_id: current_team_id, timeout: 1200)
17
17
  else
18
18
  options = {
19
19
  request: {
@@ -78,46 +78,76 @@ module Spaceship
78
78
  end
79
79
 
80
80
  def get(url_or_path, params = nil)
81
- response = request(:get) do |req|
82
- req.url(url_or_path)
83
- req.options.params_encoder = Faraday::NestedParamsEncoder
84
- req.params = params if params
85
- req.headers['Content-Type'] = 'application/json'
81
+ response = with_asc_retry do
82
+ request(:get) do |req|
83
+ req.url(url_or_path)
84
+ req.options.params_encoder = Faraday::NestedParamsEncoder
85
+ req.params = params if params
86
+ req.headers['Content-Type'] = 'application/json'
87
+ end
86
88
  end
87
89
  handle_response(response)
88
90
  end
89
91
 
90
92
  def post(url_or_path, body)
91
- response = request(:post) do |req|
92
- req.url(url_or_path)
93
- req.body = body.to_json
94
- req.headers['Content-Type'] = 'application/json'
93
+ response = with_asc_retry do
94
+ request(:post) do |req|
95
+ req.url(url_or_path)
96
+ req.body = body.to_json
97
+ req.headers['Content-Type'] = 'application/json'
98
+ end
95
99
  end
96
100
  handle_response(response)
97
101
  end
98
102
 
99
103
  def patch(url_or_path, body)
100
- response = request(:patch) do |req|
101
- req.url(url_or_path)
102
- req.body = body.to_json
103
- req.headers['Content-Type'] = 'application/json'
104
+ response = with_asc_retry do
105
+ request(:patch) do |req|
106
+ req.url(url_or_path)
107
+ req.body = body.to_json
108
+ req.headers['Content-Type'] = 'application/json'
109
+ end
104
110
  end
105
111
  handle_response(response)
106
112
  end
107
113
 
108
114
  def delete(url_or_path, params = nil, body = nil)
109
- response = request(:delete) do |req|
110
- req.url(url_or_path)
111
- req.options.params_encoder = Faraday::NestedParamsEncoder if params
112
- req.params = params if params
113
- req.body = body.to_json if body
114
- req.headers['Content-Type'] = 'application/json' if body
115
+ response = with_asc_retry do
116
+ request(:delete) do |req|
117
+ req.url(url_or_path)
118
+ req.options.params_encoder = Faraday::NestedParamsEncoder if params
119
+ req.params = params if params
120
+ req.body = body.to_json if body
121
+ req.headers['Content-Type'] = 'application/json' if body
122
+ end
115
123
  end
116
124
  handle_response(response)
117
125
  end
118
126
 
119
127
  protected
120
128
 
129
+ def with_asc_retry(tries = 5, &_block)
130
+ tries = 1 if Object.const_defined?("SpecHelper")
131
+ response = yield
132
+
133
+ tries -= 1
134
+ status = response.status if response
135
+
136
+ if [504].include?(status)
137
+ msg = "Timeout received! Retrying after 3 seconds (remaining: #{tries})..."
138
+ raise msg
139
+ end
140
+
141
+ return response
142
+ rescue => error
143
+ puts(error) if Spaceship::Globals.verbose?
144
+ if tries.zero?
145
+ return response
146
+ else
147
+ retry
148
+ end
149
+ end
150
+
121
151
  def handle_response(response)
122
152
  if (200...300).cover?(response.status) && (response.body.nil? || response.body.empty?)
123
153
  return
@@ -141,21 +171,57 @@ module Spaceship
141
171
  def handle_errors(response)
142
172
  # Example error format
143
173
  # {
144
- # "errors" : [ {
145
- # "id" : "ce8c391e-f858-411b-a14b-5aa26e0915f2",
146
- # "status" : "400",
147
- # "code" : "PARAMETER_ERROR.INVALID",
148
- # "title" : "A parameter has an invalid value",
149
- # "detail" : "'uploadedDate3' is not a valid field name",
150
- # "source" : {
151
- # "parameter" : "sort"
174
+ # "errors":[
175
+ # {
176
+ # "id":"cbfd8674-4802-4857-bfe8-444e1ea36e32",
177
+ # "status":"409",
178
+ # "code":"STATE_ERROR",
179
+ # "title":"The request cannot be fulfilled because of the state of another resource.",
180
+ # "detail":"Submit for review errors found.",
181
+ # "meta":{
182
+ # "associatedErrors":{
183
+ # "/v1/appScreenshots/":[
184
+ # {
185
+ # "id":"23d1734f-b81f-411a-98e4-6d3e763d54ed",
186
+ # "status":"409",
187
+ # "code":"STATE_ERROR.SCREENSHOT_REQUIRED.APP_WATCH_SERIES_4",
188
+ # "title":"App screenshot missing (APP_WATCH_SERIES_4)."
189
+ # },
190
+ # {
191
+ # "id":"db993030-0a93-48e9-9fd7-7e5676633431",
192
+ # "status":"409",
193
+ # "code":"STATE_ERROR.SCREENSHOT_REQUIRED.APP_WATCH_SERIES_4",
194
+ # "title":"App screenshot missing (APP_WATCH_SERIES_4)."
195
+ # }
196
+ # ],
197
+ # "/v1/builds/d710b6fa-5235-4fe4-b791-2b80d6818db0":[
198
+ # {
199
+ # "id":"e421fe6f-0e3b-464b-89dc-ba437e7bb77d",
200
+ # "status":"409",
201
+ # "code":"ENTITY_ERROR.ATTRIBUTE.REQUIRED",
202
+ # "title":"The provided entity is missing a required attribute",
203
+ # "detail":"You must provide a value for the attribute 'usesNonExemptEncryption' with this request",
204
+ # "source":{
205
+ # "pointer":"/data/attributes/usesNonExemptEncryption"
206
+ # }
207
+ # }
208
+ # ]
209
+ # }
210
+ # }
152
211
  # }
153
- # } ]
212
+ # ]
154
213
  # }
155
214
 
156
215
  return response.body['errors'].map do |error|
157
- "#{error['title']} - #{error['detail']}"
158
- end.join(" ")
216
+ messages = [[error['title'], error['detail']].compact.join(" - ")]
217
+
218
+ meta = error["meta"] || {}
219
+ associated_errors = meta["associatedErrors"] || {}
220
+
221
+ messages + associated_errors.values.flatten.map do |associated_error|
222
+ [[associated_error["title"], associated_error["detail"]].compact.join(" - ")]
223
+ end
224
+ end.flatten.join("\n")
159
225
  end
160
226
 
161
227
  private
@@ -0,0 +1,98 @@
1
+ require 'faraday' # HTTP Client
2
+ require 'faraday-cookie_jar'
3
+ require 'faraday_middleware'
4
+
5
+ require 'spaceship/globals'
6
+
7
+ module Spaceship
8
+ class ConnectAPI
9
+ module FileUploader
10
+ def self.upload(upload_operations, bytes)
11
+ # {
12
+ # "method": "PUT",
13
+ # "url": "https://some-url-apple-gives-us",
14
+ # "length": 57365,
15
+ # "offset": 0,
16
+ # "requestHeaders": [
17
+ # {
18
+ # "name": "Content-Type",
19
+ # "value": "image/png"
20
+ # }
21
+ # ]
22
+ # }
23
+
24
+ upload_operations.each_with_index do |upload_operation, index|
25
+ headers = {}
26
+ upload_operation["requestHeaders"].each do |hash|
27
+ headers[hash["name"]] = hash["value"]
28
+ end
29
+
30
+ offset = upload_operation["offset"]
31
+ length = upload_operation["length"]
32
+
33
+ puts("Uploading file (part #{index + 1})...") if Spaceship::Globals.verbose?
34
+ with_retry do
35
+ client.send(
36
+ upload_operation["method"].downcase,
37
+ upload_operation["url"],
38
+ bytes[offset, length],
39
+ headers
40
+ )
41
+ end
42
+ end
43
+ puts("Uploading complete!") if Spaceship::Globals.verbose?
44
+ end
45
+
46
+ def self.with_retry(tries = 5, &_block)
47
+ tries = 1 if Object.const_defined?("SpecHelper")
48
+ response = yield
49
+
50
+ tries -= 1
51
+
52
+ unless (200...300).cover?(response.status)
53
+ msg = "Received status of #{response.status}! Retrying after 3 seconds (remaining: #{tries})..."
54
+ raise msg
55
+ end
56
+
57
+ return response
58
+ rescue => error
59
+ puts(error) if Spaceship::Globals.verbose?
60
+ if tries.zero?
61
+ raise "Failed to upload file after retries... Received #{response.status}"
62
+ else
63
+ retry
64
+ end
65
+ end
66
+
67
+ def self.client
68
+ options = {
69
+ request: {
70
+ timeout: (ENV["SPACESHIP_TIMEOUT"] || 300).to_i,
71
+ open_timeout: (ENV["SPACESHIP_TIMEOUT"] || 300).to_i
72
+ }
73
+ }
74
+
75
+ @client ||= Faraday.new(options) do |c|
76
+ c.response(:json, content_type: /\bjson$/)
77
+ c.response(:xml, content_type: /\bxml$/)
78
+ c.response(:plist, content_type: /\bplist$/)
79
+ c.adapter(Faraday.default_adapter)
80
+
81
+ if ENV['SPACESHIP_DEBUG']
82
+ # for debugging only
83
+ # This enables tracking of networking requests using Charles Web Proxy
84
+ c.proxy = "https://127.0.0.1:8888"
85
+ c.ssl[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
86
+ elsif ENV["SPACESHIP_PROXY"]
87
+ c.proxy = ENV["SPACESHIP_PROXY"]
88
+ c.ssl[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if ENV["SPACESHIP_PROXY_SSL_VERIFY_NONE"]
89
+ end
90
+
91
+ if ENV["DEBUG"]
92
+ puts("To run spaceship through a local proxy, use SPACESHIP_DEBUG")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end