fastlane 2.102.0 → 2.103.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +74 -74
  3. data/credentials_manager/lib/credentials_manager/account_manager.rb +9 -4
  4. data/deliver/lib/assets/DeliverfileDefault.swift +10 -0
  5. data/deliver/lib/assets/summary.html.erb +3 -3
  6. data/deliver/lib/deliver/html_generator.rb +9 -0
  7. data/deliver/lib/deliver/options.rb +56 -35
  8. data/deliver/lib/deliver/setup.rb +1 -2
  9. data/deliver/lib/deliver/submit_for_review.rb +2 -2
  10. data/deliver/lib/deliver/upload_metadata.rb +1 -1
  11. data/fastlane/lib/fastlane/action.rb +1 -0
  12. data/fastlane/lib/fastlane/actions/actions_helper.rb +11 -1
  13. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +1 -1
  14. data/fastlane/lib/fastlane/actions/changelog_from_git_commits.rb +11 -3
  15. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +1 -1
  16. data/fastlane/lib/fastlane/actions/create_app_online.rb +1 -1
  17. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +15 -1
  18. data/fastlane/lib/fastlane/actions/docs/create_app_online.md +21 -0
  19. data/fastlane/lib/fastlane/actions/download_dsyms.rb +1 -1
  20. data/fastlane/lib/fastlane/actions/gradle.rb +4 -0
  21. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +1 -1
  22. data/fastlane/lib/fastlane/actions/set_changelog.rb +1 -1
  23. data/fastlane/lib/fastlane/documentation/markdown_docs_generator.rb +2 -0
  24. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -0
  25. data/fastlane/lib/fastlane/setup/setup_ios.rb +7 -5
  26. data/fastlane/lib/fastlane/version.rb +1 -1
  27. data/fastlane/swift/Deliverfile.swift +1 -1
  28. data/fastlane/swift/DeliverfileProtocol.swift +13 -13
  29. data/fastlane/swift/Fastlane.swift +45 -43
  30. data/fastlane/swift/Gymfile.swift +1 -1
  31. data/fastlane/swift/Matchfile.swift +1 -1
  32. data/fastlane/swift/Precheckfile.swift +1 -1
  33. data/fastlane/swift/Scanfile.swift +1 -1
  34. data/fastlane/swift/Screengrabfile.swift +1 -1
  35. data/fastlane/swift/Snapshotfile.swift +1 -1
  36. data/fastlane_core/lib/fastlane_core/command_executor.rb +9 -14
  37. data/fastlane_core/lib/fastlane_core/fastlane_pty.rb +12 -3
  38. data/fastlane_core/lib/fastlane_core/helper.rb +22 -10
  39. data/fastlane_core/lib/fastlane_core/ipa_upload_package_builder.rb +1 -1
  40. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +22 -15
  41. data/pilot/lib/pilot/build_manager.rb +1 -1
  42. data/pilot/lib/pilot/tester_importer.rb +2 -2
  43. data/precheck/lib/precheck/rules/unreachable_urls_rule.rb +3 -3
  44. data/produce/lib/produce/cloud_container.rb +82 -0
  45. data/produce/lib/produce/commands_generator.rb +34 -0
  46. data/produce/lib/produce/itunes_connect.rb +1 -1
  47. data/snapshot/lib/snapshot/commands_generator.rb +0 -2
  48. data/snapshot/lib/snapshot/reports_generator.rb +8 -3
  49. data/snapshot/lib/snapshot/runner.rb +9 -0
  50. data/spaceship/README.md +2 -2
  51. data/spaceship/lib/spaceship/client.rb +6 -2
  52. data/spaceship/lib/spaceship/errors.rb +3 -0
  53. data/spaceship/lib/spaceship/portal/app.rb +22 -1
  54. data/spaceship/lib/spaceship/portal/cloud_container.rb +74 -0
  55. data/spaceship/lib/spaceship/portal/portal.rb +1 -0
  56. data/spaceship/lib/spaceship/portal/portal_client.rb +45 -4
  57. data/spaceship/lib/spaceship/portal/spaceship.rb +9 -0
  58. data/spaceship/lib/spaceship/test_flight/client.rb +1 -1
  59. data/spaceship/lib/spaceship/tunes/app_ratings.rb +2 -2
  60. data/spaceship/lib/spaceship/tunes/app_submission.rb +9 -3
  61. data/spaceship/lib/spaceship/tunes/app_version.rb +1 -1
  62. data/spaceship/lib/spaceship/tunes/application.rb +4 -4
  63. data/spaceship/lib/spaceship/tunes/tunes_client.rb +13 -2
  64. data/supply/lib/supply/client.rb +0 -1
  65. metadata +22 -20
@@ -78,16 +78,21 @@ module Snapshot
78
78
  'iPhone 6 Plus' => "iPhone 6 Plus (5.5-Inch)",
79
79
  'iPhone 6s' => "iPhone 6s (4.7-Inch)",
80
80
  'iPhone 6' => "iPhone 6 (4.7-Inch)",
81
- 'iPhone 5s' => "iPhone 5 (4-Inch)",
81
+ 'iPhone 5s' => "iPhone 5s (4-Inch)",
82
+ 'iPhone 5' => "iPhone 5 (4-Inch)",
82
83
  'iPhone SE' => "iPhone SE",
83
- 'iPhone 4s' => "iPhone 4 (3.5-Inch)",
84
- 'iPad Air' => 'iPad Air',
84
+ 'iPhone 4s' => "iPhone 4s (3.5-Inch)",
85
+ 'iPad 2' => 'iPad 2',
85
86
  'iPad Air 2' => 'iPad Air 2',
87
+ 'iPad Air' => 'iPad Air',
86
88
  'iPad (5th generation)' => 'iPad (5th generation)',
87
89
  'iPad Pro (9.7-inch)' => 'iPad Pro (9.7-inch)',
90
+ 'iPad Pro (9.7 inch)' => 'iPad Pro (9.7-inch)', # iOS 10.3.1 simulator
88
91
  'iPad Pro (10.5-inch)' => 'iPad Pro (10.5-inch)',
89
92
  'iPad Pro (12.9-inch) (2nd generation)' => 'iPad Pro (12.9-inch) (2nd generation)',
90
93
  'iPad Pro (12.9-inch)' => 'iPad Pro (12.9-inch)',
94
+ 'iPad Pro (12.9 inch)' => 'iPad Pro (12.9-inch)', # iOS 10.3.1 simulator
95
+ 'iPad Pro' => 'iPad Pro (12.9-inch)', # iOS 9.3 simulator
91
96
  'Apple TV 1080p' => 'Apple TV',
92
97
  'Apple TV 4K (at 1080p)' => 'Apple TV 4K (at 1080p)',
93
98
  'Apple TV 4K' => 'Apple TV 4K',
@@ -122,6 +122,15 @@ module Snapshot
122
122
  UI.verbose("Checking that helper files contain #{current_version}")
123
123
 
124
124
  helper_files = Update.find_helper
125
+ if helper_files.empty?
126
+ UI.error("Your Snapshot Helper file is missing, please place a copy")
127
+ UI.error("in your project directory")
128
+ UI.message("More information about Snapshot setup can be found here:")
129
+ UI.message("https://docs.fastlane.tools/actions/snapshot/#quick-start")
130
+ UI.user_error!("Please add a Snapshot Helper file to your project")
131
+ return
132
+ end
133
+
125
134
  helper_files.each do |path|
126
135
  content = File.read(path)
127
136
 
@@ -177,12 +177,12 @@ Overview of the used API endpoints
177
177
  - Repair provisioning profiles
178
178
  - Download provisioning profiles
179
179
  - Team selection
180
- - `https://itunesconnect.apple.com`:
180
+ - `https://appstoreconnect.apple.com`:
181
181
  - Managing apps
182
182
  - Managing beta testers
183
183
  - Submitting updates to review
184
184
  - Managing app metadata
185
- - `https://du-itc.itunesconnect.apple.com`:
185
+ - `https://du-itc.appstoreconnect.apple.com`:
186
186
  - Upload icons, screenshots, trailers ...
187
187
 
188
188
  _spaceship_ uses all those API points to offer this seamless experience.
@@ -50,6 +50,7 @@ module Spaceship
50
50
  UnexpectedResponse = Spaceship::UnexpectedResponse
51
51
  AppleTimeoutError = Spaceship::AppleTimeoutError
52
52
  UnauthorizedAccessError = Spaceship::UnauthorizedAccessError
53
+ GatewayTimeoutError = Spaceship::GatewayTimeoutError
53
54
  InternalServerError = Spaceship::InternalServerError
54
55
 
55
56
  # Authenticates with Apple's web services. This method has to be called once
@@ -469,7 +470,7 @@ module Spaceship
469
470
  elsif response.status == 412 && AUTH_TYPES.include?(response.body["authType"])
470
471
  # Need to acknowledge Apple ID and Privacy statement - https://github.com/fastlane/fastlane/issues/12577
471
472
  # Looking for status of 412 might be enough but might be safer to keep looking only at what is being reported
472
- raise AppleIDAndPrivacyAcknowledgementNeeded.new, "Need to acknowledge to Apple's Apple ID and Privacy statement. Please manually log into https://appleid.apple.com (or https://itunesconnect.apple.com) to acknowledge the statement."
473
+ raise AppleIDAndPrivacyAcknowledgementNeeded.new, "Need to acknowledge to Apple's Apple ID and Privacy statement. Please manually log into https://appleid.apple.com (or https://appstoreconnect.apple.com) to acknowledge the statement."
473
474
  elsif (response['Set-Cookie'] || "").include?("itctx")
474
475
  raise "Looks like your Apple ID is not enabled for App Store Connect, make sure to be able to login online"
475
476
  else
@@ -507,7 +508,7 @@ module Spaceship
507
508
  itc_service_key_path = "/tmp/spaceship_itc_service_key.txt"
508
509
  return File.read(itc_service_key_path) if File.exist?(itc_service_key_path)
509
510
 
510
- response = request(:get, "https://olympus.itunes.apple.com/v1/app/config?hostname=itunesconnect.apple.com")
511
+ response = request(:get, "https://olympus.itunes.apple.com/v1/app/config?hostname=appstoreconnect.apple.com")
511
512
  @service_key = response.body["authServiceKey"].to_s
512
513
 
513
514
  raise "Service key is empty" if @service_key.length == 0
@@ -532,6 +533,7 @@ module Spaceship
532
533
  Faraday::Error::TimeoutError,
533
534
  Faraday::ParsingError, # <h2>Internal Server Error</h2> with content type json
534
535
  AppleTimeoutError,
536
+ GatewayTimeoutError,
535
537
  InternalServerError => ex # New Faraday version: Faraday::TimeoutError => ex
536
538
  tries -= 1
537
539
  unless tries.zero?
@@ -628,6 +630,8 @@ module Spaceship
628
630
  raise_insuffient_permission_error!(caller_location: 3)
629
631
  elsif body.to_s.include?("Internal Server Error - Read")
630
632
  raise InternalServerError, "Received an internal server error from App Store Connect / Developer Portal, please try again later"
633
+ elsif body.to_s.include?("Gateway Timeout - In read")
634
+ raise GatewayTimeoutError, "Received a gateway timeout error from App Store Connect / Developer Portal, please try again later"
631
635
  elsif (body["resultString"] || "").include?("Program License Agreement")
632
636
  raise ProgramLicenseAgreementUpdated, "#{body['userString']} Please manually log into your Apple Developer account to review and accept the updated agreement."
633
637
  end
@@ -70,4 +70,7 @@ module Spaceship
70
70
 
71
71
  # Raised when 500 is received from App Store Connect
72
72
  class InternalServerError < BasicPreferredInfoError; end
73
+
74
+ # Raised when 504 is received from App Store Connect
75
+ class GatewayTimeoutError < BasicPreferredInfoError; end
73
76
  end
@@ -1,5 +1,6 @@
1
1
  require_relative 'portal_base'
2
2
  require_relative 'app_group'
3
+ require_relative 'cloud_container'
3
4
 
4
5
  module Spaceship
5
6
  module Portal
@@ -57,6 +58,9 @@ module Spaceship
57
58
  # @return (Array of Spaceship::Portal::AppGroup) Associated groups
58
59
  attr_accessor :associated_groups
59
60
 
61
+ # @return (Array of Spaceship::Portal::CloudContainer) Associated cloud containers
62
+ attr_accessor :associated_cloud_containers
63
+
60
64
  attr_mapping(
61
65
  'appIdId' => :app_id,
62
66
  'name' => :name,
@@ -70,7 +74,8 @@ module Spaceship
70
74
  'isProdPushEnabled' => :prod_push_enabled,
71
75
  'associatedApplicationGroupsCount' => :app_groups_count,
72
76
  'associatedCloudContainersCount' => :cloud_containers_count,
73
- 'associatedIdentifiersCount' => :identifiers_count
77
+ 'associatedIdentifiersCount' => :identifiers_count,
78
+ 'associatedCloudContainers' => :associated_cloud_containers
74
79
  )
75
80
 
76
81
  class << self
@@ -117,6 +122,14 @@ module Spaceship
117
122
  end
118
123
  end
119
124
 
125
+ def associated_cloud_containers
126
+ return unless raw_data['associatedCloudContainers']
127
+
128
+ @associated_cloud_containers ||= raw_data['associatedCloudContainers'].map do |info|
129
+ Spaceship::Portal::CloudContainer.new(info)
130
+ end
131
+ end
132
+
120
133
  # Delete this App ID. This action will most likely fail if the App ID is already in the store
121
134
  # or there are active profiles
122
135
  # @return (App) The app you just deletd
@@ -147,6 +160,14 @@ module Spaceship
147
160
  self.class.factory(app)
148
161
  end
149
162
 
163
+ # Associate specific iCloud Containers with this app
164
+ # @return (App) The updated detailed app. This is nil if the app couldn't be found.
165
+ def associate_cloud_containers(containers)
166
+ raise "`associate_cloud_containers` not available for Mac apps" if mac?
167
+ app = client.associate_cloud_containers_with_app(self, containers)
168
+ self.class.factory(app)
169
+ end
170
+
150
171
  # Associate specific merchants with this app
151
172
  # @return (App) The updated detailed app. This is nil if the app couldn't be found
152
173
  def associate_merchants(merchants)
@@ -0,0 +1,74 @@
1
+ require_relative 'portal_base'
2
+
3
+ module Spaceship
4
+ module Portal
5
+ # Represents an iCloud Container of the Apple Dev Portal
6
+ class CloudContainer < PortalBase
7
+ # @return (String) The identifier assigned to this container
8
+ # @example
9
+ # "iCloud.com.example.application"
10
+ attr_accessor :identifier
11
+
12
+ # @return (String) The prefix assigned to this container
13
+ # @example
14
+ # "9J57U9392R"
15
+ attr_accessor :prefix
16
+
17
+ # @return (String) The name of this container
18
+ # @example
19
+ # "iCloud com example application"
20
+ attr_accessor :name
21
+
22
+ # @return (String) Status of the container
23
+ # @example
24
+ # "current"
25
+ attr_accessor :status
26
+
27
+ # @return (String) The identifier of this iCloud container, provided by the Dev Portal
28
+ # @example
29
+ # "2MAY7NPHAA"
30
+ attr_accessor :cloud_container
31
+
32
+ # @return (Bool) Is the container editable?
33
+ attr_accessor :can_edit
34
+
35
+ # @return (Bool) Is the container deletable?
36
+ attr_accessor :can_delete
37
+
38
+ attr_mapping(
39
+ 'identifier' => :identifier,
40
+ 'prefix' => :prefix,
41
+ 'name' => :name,
42
+ 'cloudContainer' => :cloud_container,
43
+ 'status' => :status,
44
+ 'canEdit' => :can_edit,
45
+ 'canDelete' => :can_delete
46
+ )
47
+
48
+ class << self
49
+ # @return (Array) Returns all iCloud containers available for this account
50
+ def all
51
+ client.cloud_containers.map { |container| self.factory(container) }
52
+ end
53
+
54
+ # Creates a new iCloud Container on the Apple Dev Portal
55
+ #
56
+ # @param identifier [String] the identifier to assign to this container
57
+ # @param name [String] the name of the container
58
+ # @return (CloudContainer) The container you just created
59
+ def create!(identifier: nil, name: nil)
60
+ new_container = client.create_cloud_container!(name, identifier)
61
+ self.new(new_container)
62
+ end
63
+
64
+ # Find a specific iCloud Container identifier
65
+ # @return (CloudContainer) The iCloud Container you're looking for. This is nil if the container can't be found.
66
+ def find(identifier)
67
+ all.find do |container|
68
+ container.identifier == identifier
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -4,6 +4,7 @@ require 'spaceship/portal/website_push'
4
4
  require 'spaceship/portal/app_group'
5
5
  require 'spaceship/portal/app_service'
6
6
  require 'spaceship/portal/certificate'
7
+ require 'spaceship/portal/cloud_container'
7
8
  require 'spaceship/portal/device'
8
9
  require 'spaceship/portal/provisioning_profile'
9
10
  require 'spaceship/portal/portal_client'
@@ -2,6 +2,7 @@ require_relative '../client'
2
2
 
3
3
  require_relative 'app'
4
4
  require_relative 'app_group'
5
+ require_relative 'cloud_container'
5
6
  require_relative 'device'
6
7
  require_relative 'merchant'
7
8
  require_relative 'passbook'
@@ -149,6 +150,18 @@ module Spaceship
149
150
  details_for_app(app)
150
151
  end
151
152
 
153
+ def associate_cloud_containers_with_app(app, containers)
154
+ ensure_csrf(Spaceship::Portal::CloudContainer)
155
+
156
+ request(:post, 'account/ios/identifiers/assignCloudContainerToAppId.action', {
157
+ teamId: team_id,
158
+ appIdId: app.app_id,
159
+ cloudContainers: containers.map(&:cloud_container)
160
+ })
161
+
162
+ details_for_app(app)
163
+ end
164
+
152
165
  def associate_merchants_with_app(app, merchants, mac)
153
166
  ensure_csrf(Spaceship::Portal::Merchant)
154
167
 
@@ -375,6 +388,37 @@ module Spaceship
375
388
  parse_response(r)
376
389
  end
377
390
 
391
+ #####################################################
392
+ # @!group Cloud Containers
393
+ #####################################################
394
+
395
+ def cloud_containers
396
+ paging do |page_number|
397
+ r = request(:post, 'account/cloudContainer/listCloudContainers.action', {
398
+ teamId: team_id,
399
+ pageNumber: page_number,
400
+ pageSize: page_size,
401
+ sort: 'name=asc'
402
+ })
403
+ result = parse_response(r, 'cloudContainerList')
404
+
405
+ csrf_cache[Spaceship::Portal::CloudContainer] = self.csrf_tokens
406
+
407
+ result
408
+ end
409
+ end
410
+
411
+ def create_cloud_container!(name, identifier)
412
+ ensure_csrf(Spaceship::Portal::CloudContainer)
413
+
414
+ r = request(:post, 'account/cloudContainer/addCloudContainer.action', {
415
+ name: valid_name_for(name),
416
+ identifier: identifier,
417
+ teamId: team_id
418
+ })
419
+ parse_response(r, 'cloudContainer')
420
+ end
421
+
378
422
  #####################################################
379
423
  # @!group Team
380
424
  #####################################################
@@ -656,10 +700,7 @@ module Spaceship
656
700
  end
657
701
 
658
702
  def delete_provisioning_profile!(profile_id, mac: false)
659
- ensure_csrf(Spaceship::Portal::ProvisioningProfile) do
660
- fetch_csrf_token_for_provisioning
661
- end
662
-
703
+ fetch_csrf_token_for_provisioning
663
704
  r = request(:post, "account/#{platform_slug(mac)}/profile/deleteProvisioningProfile.action", {
664
705
  teamId: team_id,
665
706
  provisioningProfileId: profile_id
@@ -70,6 +70,11 @@ module Spaceship
70
70
  Spaceship::Portal::AppService
71
71
  end
72
72
 
73
+ # @return (Class) Access the iCloud Containers for the spaceship
74
+ def cloud_container
75
+ Spaceship::Portal::CloudContainer.set_client(@client)
76
+ end
77
+
73
78
  # @return (Class) Access the devices for the spaceship
74
79
  def device
75
80
  Spaceship::Portal::Device.set_client(@client)
@@ -122,6 +127,10 @@ module Spaceship
122
127
  Spaceship::Portal.app_service
123
128
  end
124
129
 
130
+ def cloud_container
131
+ Spaceship::Portal.cloud_container
132
+ end
133
+
125
134
  def device
126
135
  Spaceship::Portal.device
127
136
  end
@@ -15,7 +15,7 @@ module Spaceship
15
15
  # Each request method should make only one request. For more high-level logic, put code in the data models.
16
16
 
17
17
  def self.hostname
18
- 'https://itunesconnect.apple.com/testflight/v2/'
18
+ 'https://appstoreconnect.apple.com/testflight/v2/'
19
19
  end
20
20
 
21
21
  ##
@@ -46,8 +46,8 @@ module Spaceship
46
46
  end
47
47
 
48
48
  # @return (Array) of Review Objects
49
- def reviews(store_front = '', version_id = '')
50
- raw_reviews = client.get_reviews(application.apple_id, application.platform, store_front, version_id)
49
+ def reviews(store_front = '', version_id = '', upto_date = nil)
50
+ raw_reviews = client.get_reviews(application.apple_id, application.platform, store_front, version_id, upto_date)
51
51
  raw_reviews.map do |review|
52
52
  review["value"]["application"] = self.application
53
53
  AppReview.factory(review["value"])
@@ -12,6 +12,11 @@ module Spaceship
12
12
  # @return (AppVersion) The version to use for this submission
13
13
  attr_accessor :version
14
14
 
15
+ # @return (String) The platform of the device. This is usually "ios"
16
+ # @example
17
+ # "appletvos"
18
+ attr_accessor :platform
19
+
15
20
  # @return (Boolean) Submitted for Review
16
21
  attr_accessor :submitted_for_review
17
22
 
@@ -115,10 +120,11 @@ module Spaceship
115
120
  end
116
121
 
117
122
  # @param application (Spaceship::Tunes::Application) The app this submission is for
118
- def create(application, version)
119
- attrs = client.prepare_app_submissions(application.apple_id, application.edit_version.version_id)
123
+ def create(application, version, platform: nil)
124
+ attrs = client.prepare_app_submissions(application.apple_id, application.edit_version(platform: platform).version_id)
120
125
  attrs[:application] = application
121
126
  attrs[:version] = version
127
+ attrs[:platform] = platform
122
128
 
123
129
  return self.factory(attrs)
124
130
  end
@@ -142,7 +148,7 @@ module Spaceship
142
148
  )
143
149
  end
144
150
 
145
- client.send_app_submission(application.apple_id, application.edit_version.version_id, raw_data_clone)
151
+ client.send_app_submission(application.apple_id, application.edit_version(platform: platform).version_id, raw_data_clone)
146
152
  @submitted_for_review = true
147
153
  end
148
154
 
@@ -405,7 +405,7 @@ module Spaceship
405
405
 
406
406
  # @return (String) An URL to this specific resource. You can enter this URL into your browser
407
407
  def url
408
- url = "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/#{application.apple_id}/#{self.platform}/versioninfo/"
408
+ url = "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/#{application.apple_id}/#{self.platform}/versioninfo/"
409
409
  url += "deliverable" if self.is_live?
410
410
  return url
411
411
  end
@@ -138,7 +138,7 @@ module Spaceship
138
138
 
139
139
  # @return (String) An URL to this specific resource. You can enter this URL into your browser
140
140
  def url
141
- "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/#{self.apple_id}"
141
+ "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/#{self.apple_id}"
142
142
  end
143
143
 
144
144
  def analytics
@@ -357,13 +357,13 @@ module Spaceship
357
357
  # @!group Submit for Review
358
358
  #####################################################
359
359
 
360
- def create_submission
361
- version = self.latest_version
360
+ def create_submission(platform: nil)
361
+ version = self.latest_version(platform: platform)
362
362
  if version.nil?
363
363
  raise "Could not find a valid version to submit for review"
364
364
  end
365
365
 
366
- Spaceship::Tunes::AppSubmission.create(self, version)
366
+ Spaceship::Tunes::AppSubmission.create(self, version, platform: platform)
367
367
  end
368
368
 
369
369
  # Cancels all ongoing TestFlight beta submission for this application
@@ -50,7 +50,7 @@ module Spaceship
50
50
  #####################################################
51
51
 
52
52
  def self.hostname
53
- "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/"
53
+ "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/"
54
54
  end
55
55
 
56
56
  # Shows a team selection for the user in the terminal. This should not be
@@ -358,10 +358,13 @@ module Spaceship
358
358
  parse_response(r, 'data')
359
359
  end
360
360
 
361
- def get_reviews(app_id, platform, storefront, version_id)
361
+ def get_reviews(app_id, platform, storefront, version_id, upto_date = nil)
362
362
  index = 0
363
363
  per_page = 100 # apple default
364
364
  all_reviews = []
365
+
366
+ upto_date = Time.parse(upto_date) unless upto_date.nil?
367
+
365
368
  loop do
366
369
  rating_url = "ra/apps/#{app_id}/platforms/#{platform}/reviews?"
367
370
  rating_url << "sort=REVIEW_SORT_ORDER_MOST_RECENT"
@@ -371,12 +374,20 @@ module Spaceship
371
374
 
372
375
  r = request(:get, rating_url)
373
376
  all_reviews.concat(parse_response(r, 'data')['reviews'])
377
+ last_review_date = Time.at(all_reviews[-1]['value']['lastModified'] / 1000)
378
+
379
+ if upto_date && last_review_date < upto_date
380
+ all_reviews = all_reviews.select { |review| Time.at(review['value']['lastModified'] / 1000) > upto_date }
381
+ break
382
+ end
383
+
374
384
  if all_reviews.count < parse_response(r, 'data')['reviewCount']
375
385
  index += per_page
376
386
  else
377
387
  break
378
388
  end
379
389
  end
390
+
380
391
  all_reviews
381
392
  end
382
393