fastlane 2.150.0.rc3 → 2.150.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +76 -76
  3. data/deliver/lib/deliver/download_screenshots.rb +48 -26
  4. data/deliver/lib/deliver/runner.rb +4 -1
  5. data/deliver/lib/deliver/submit_for_review.rb +26 -7
  6. data/deliver/lib/deliver/upload_metadata.rb +29 -10
  7. data/deliver/lib/deliver/upload_screenshots.rb +49 -10
  8. data/fastlane/lib/fastlane/actions/.hockey.rb.swp +0 -0
  9. data/fastlane/lib/fastlane/actions/.slack.rb.swp +0 -0
  10. data/fastlane/lib/fastlane/actions/.update_project_provisioning.rb.swp +0 -0
  11. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +4 -0
  12. data/fastlane/lib/fastlane/version.rb +1 -1
  13. data/fastlane/swift/Deliverfile.swift +1 -1
  14. data/fastlane/swift/DeliverfileProtocol.swift +6 -6
  15. data/fastlane/swift/Fastlane.swift +22 -19
  16. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/josh.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  17. data/fastlane/swift/Gymfile.swift +1 -1
  18. data/fastlane/swift/Matchfile.swift +1 -1
  19. data/fastlane/swift/Precheckfile.swift +1 -1
  20. data/fastlane/swift/Scanfile.swift +1 -1
  21. data/fastlane/swift/Screengrabfile.swift +1 -1
  22. data/fastlane/swift/Snapshotfile.swift +1 -1
  23. data/fastlane/swift/SocketClient.swift +2 -1
  24. data/fastlane_core/lib/fastlane_core/build_watcher.rb +4 -4
  25. data/fastlane_core/lib/fastlane_core/ipa_file_analyser.rb +0 -1
  26. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +89 -52
  27. data/fastlane_core/lib/fastlane_core/provisioning_profile.rb +1 -1
  28. data/pilot/lib/pilot/.manager.rb.swp +0 -0
  29. data/produce/lib/produce/itunes_connect.rb +29 -2
  30. data/spaceship/lib/spaceship/{.DS_Store → connect_api/.DS_Store} +0 -0
  31. data/spaceship/lib/spaceship/connect_api/client.rb +1 -1
  32. data/spaceship/lib/spaceship/connect_api/file_uploader.rb +42 -10
  33. data/spaceship/lib/spaceship/connect_api/models/age_rating_declaration.rb +1 -1
  34. data/spaceship/lib/spaceship/connect_api/models/app.rb +46 -6
  35. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +4 -0
  36. data/spaceship/lib/spaceship/connect_api/models/app_preview.rb +62 -10
  37. data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +2 -2
  38. data/spaceship/lib/spaceship/connect_api/models/app_review_attachment.rb +18 -28
  39. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +86 -37
  40. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +32 -2
  41. data/spaceship/lib/spaceship/connect_api/models/app_store_review_detail.rb +5 -6
  42. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +22 -3
  43. data/spaceship/lib/spaceship/connect_api/models/user.rb +2 -1
  44. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +41 -13
  45. data/spaceship/lib/spaceship/connect_api/users/users.rb +13 -0
  46. metadata +37 -32
@@ -113,7 +113,7 @@ module FastlaneCore
113
113
  else
114
114
  # `security` only works on Mac, fallback to `openssl`
115
115
  # via https://stackoverflow.com/a/14379814/252627
116
- decoded = `openssl smime -inform der -verify -noverify -in #{path} 2> #{err}`
116
+ decoded = `openssl smime -inform der -verify -noverify -in #{path.shellescape} 2> #{err}`
117
117
  end
118
118
  UI.error("Failure to decode #{path}. Exit: #{$?.exitstatus}: #{File.read(err)}") if $?.exitstatus != 0
119
119
  decoded
@@ -21,6 +21,13 @@ module Produce
21
21
  UI.success("App '#{Produce.config[:app_identifier]}' already exists (#{application.id}), nothing to do on App Store Connect")
22
22
  # Nothing to do here
23
23
  else
24
+ emails = Produce.config[:itc_users] || []
25
+ user_ids = []
26
+ unless emails.empty?
27
+ UI.message("Verifying users exist before creating app...")
28
+ user_ids = find_user_ids(emails: emails)
29
+ end
30
+
24
31
  UI.success("Creating new app '#{Produce.config[:app_name]}' on App Store Connect")
25
32
 
26
33
  platforms = Produce.config[:platforms] || [Produce.config[:platform]]
@@ -29,7 +36,6 @@ module Produce
29
36
  Spaceship::ConnectAPI::Platform.map(platform)
30
37
  end
31
38
 
32
- # Produce.config[:company_name]
33
39
  # Produce.config[:itc_users]
34
40
  application = Spaceship::ConnectAPI::App.create(
35
41
  name: Produce.config[:app_name],
@@ -37,7 +43,8 @@ module Produce
37
43
  sku: Produce.config[:sku].to_s,
38
44
  primary_locale: language,
39
45
  bundle_id: app_identifier,
40
- platforms: platforms
46
+ platforms: platforms,
47
+ company_name: Produce.config[:company_name]
41
48
  )
42
49
 
43
50
  application = fetch_application
@@ -60,12 +67,32 @@ module Produce
60
67
  application.ensure_version!(Produce.config[:app_version], platform: platform) if Produce.config[:app_version]
61
68
  end
62
69
 
70
+ # Add users to app
71
+ unless user_ids.empty?
72
+ application.add_users(user_ids: user_ids)
73
+ UI.message("Successfuly added #{user_ids.size} #{user_ids.count == 1 ? 'user' : 'users'} to app")
74
+ end
75
+
63
76
  UI.success("Successfully created new app '#{Produce.config[:app_name]}' on App Store Connect with ID #{application.id}")
64
77
  end
65
78
 
66
79
  return application.id
67
80
  end
68
81
 
82
+ def find_user_ids(emails: nil)
83
+ emails ||= []
84
+ users = Spaceship::ConnectAPI::User.all.select do |user|
85
+ emails.include?(user.email)
86
+ end
87
+
88
+ diff_emails = emails - users.map(&:email)
89
+ unless diff_emails.empty?
90
+ raise "Could not find users with emails of: #{diff_emails.join(',')}"
91
+ end
92
+
93
+ return users.map(&:id)
94
+ end
95
+
69
96
  private
70
97
 
71
98
  def platform
@@ -133,7 +133,7 @@ module Spaceship
133
133
  tries -= 1
134
134
  status = response.status if response
135
135
 
136
- if [504].include?(status)
136
+ if [500, 504].include?(status)
137
137
  msg = "Timeout received! Retrying after 3 seconds (remaining: #{tries})..."
138
138
  raise msg
139
139
  end
@@ -2,10 +2,12 @@ require 'faraday' # HTTP Client
2
2
  require 'faraday-cookie_jar'
3
3
  require 'faraday_middleware'
4
4
 
5
+ require 'spaceship/globals'
6
+
5
7
  module Spaceship
6
8
  class ConnectAPI
7
9
  module FileUploader
8
- def self.upload(upload_operation, payload)
10
+ def self.upload(upload_operations, bytes)
9
11
  # {
10
12
  # "method": "PUT",
11
13
  # "url": "https://some-url-apple-gives-us",
@@ -19,17 +21,47 @@ module Spaceship
19
21
  # ]
20
22
  # }
21
23
 
22
- headers = {}
23
- upload_operation["requestHeaders"].each do |hash|
24
- headers[hash["name"]] = hash["value"]
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
25
42
  end
43
+ puts("Uploading complete!") if Spaceship::Globals.verbose?
44
+ end
26
45
 
27
- client.send(
28
- upload_operation["method"].downcase,
29
- upload_operation["url"],
30
- payload,
31
- headers
32
- )
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
33
65
  end
34
66
 
35
67
  def self.client
@@ -95,7 +95,7 @@ module Spaceship
95
95
  return value if new_value.nil?
96
96
  return new_value
97
97
  else
98
- return LEGACY_RATING_VALUE_ITC_MAP[value]
98
+ return LEGACY_RATING_VALUE_ITC_MAP[value] || value
99
99
  end
100
100
 
101
101
  return value
@@ -54,14 +54,15 @@ module Spaceship
54
54
  end
55
55
  end
56
56
 
57
- def self.create(name: nil, version_string: nil, sku: nil, primary_locale: nil, bundle_id: nil, platforms: nil)
57
+ def self.create(name: nil, version_string: nil, sku: nil, primary_locale: nil, bundle_id: nil, platforms: nil, company_name: nil)
58
58
  Spaceship::ConnectAPI.post_app(
59
59
  name: name,
60
60
  version_string: version_string,
61
61
  sku: sku,
62
62
  primary_locale: primary_locale,
63
63
  bundle_id: bundle_id,
64
- platforms: platforms
64
+ platforms: platforms,
65
+ company_name: company_name
65
66
  )
66
67
  end
67
68
 
@@ -82,10 +83,13 @@ module Spaceship
82
83
  Spaceship::ConnectAPI::AppInfo::AppStoreState::PREPARE_FOR_SUBMISSION,
83
84
  Spaceship::ConnectAPI::AppInfo::AppStoreState::DEVELOPER_REJECTED,
84
85
  Spaceship::ConnectAPI::AppInfo::AppStoreState::REJECTED,
85
- Spaceship::ConnectAPI::AppInfo::AppStoreState::METADATA_REJECTED
86
+ Spaceship::ConnectAPI::AppInfo::AppStoreState::METADATA_REJECTED,
87
+ Spaceship::ConnectAPI::AppInfo::AppStoreState::WAITING_FOR_REVIEW,
88
+ Spaceship::ConnectAPI::AppInfo::AppStoreState::INVALID_BINARY
86
89
  ]
87
90
 
88
- resp = Spaceship::ConnectAPI.get_app_infos(app_id: id)
91
+ filter = { app: id }
92
+ resp = Spaceship::ConnectAPI.get_app_infos(filter: filter)
89
93
  return resp.to_models.select do |model|
90
94
  states.include?(model.app_store_state)
91
95
  end.first
@@ -106,6 +110,26 @@ module Spaceship
106
110
  # App Store Versions
107
111
  #
108
112
 
113
+ def reject_version_if_possible!
114
+ platform ||= Spaceship::ConnectAPI::Platform::IOS
115
+ filter = {
116
+ appStoreState: [
117
+ Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PENDING_DEVELOPER_RELEASE,
118
+ Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::IN_REVIEW,
119
+ Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::WAITING_FOR_REVIEW
120
+ ].join(","),
121
+ platform: platform
122
+ }
123
+
124
+ # Get the latest version
125
+ version = get_app_store_versions(filter: filter, includes: "appStoreVersionSubmission")
126
+ .sort_by { |v| Gem::Version.new(v.version_string) }
127
+ .last
128
+
129
+ return false if version.nil?
130
+ return version.reject!
131
+ end
132
+
109
133
  # Will make sure the current edit_version matches the given version number
110
134
  # This will either create a new version or change the version number
111
135
  # from an existing version
@@ -144,11 +168,17 @@ module Spaceship
144
168
  Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PREPARE_FOR_SUBMISSION,
145
169
  Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::DEVELOPER_REJECTED,
146
170
  Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::REJECTED,
147
- Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::METADATA_REJECTED
171
+ Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::METADATA_REJECTED,
172
+ Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::WAITING_FOR_REVIEW,
173
+ Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::INVALID_BINARY
148
174
  ].join(","),
149
175
  platform: platform
150
176
  }
151
- return get_app_store_versions(filter: filter, includes: includes).first
177
+
178
+ # Get the latest version
179
+ return get_app_store_versions(filter: filter, includes: includes)
180
+ .sort_by { |v| Gem::Version.new(v.version_string) }
181
+ .last
152
182
  end
153
183
 
154
184
  def get_app_store_versions(filter: {}, includes: nil, limit: nil, sort: nil)
@@ -226,6 +256,16 @@ module Spaceship
226
256
  ).all_pages
227
257
  return resps.flat_map(&:to_models).first
228
258
  end
259
+
260
+ #
261
+ # Users
262
+ #
263
+
264
+ def add_users(user_ids: nil)
265
+ user_ids.each do |user_id|
266
+ Spaceship::ConnectAPI.add_user_visible_apps(user_id: user_id, app_ids: [id])
267
+ end
268
+ end
229
269
  end
230
270
  end
231
271
  end
@@ -11,11 +11,15 @@ module Spaceship
11
11
 
12
12
  module AppStoreState
13
13
  READY_FOR_SALE = "READY_FOR_SALE"
14
+ PROCESSING_FOR_APP_STORE = "PROCESSING_FOR_APP_STORE"
15
+ PENDING_DEVELOPER_RELEASE = "PENDING_DEVELOPER_RELEASE"
16
+ IN_REVIEW = "IN_REVIEW"
14
17
  WAITING_FOR_REVIEW = "WAITING_FOR_REVIEW"
15
18
  DEVELOPER_REJECTED = "DEVELOPER_REJECTED"
16
19
  REJECTED = "REJECTED"
17
20
  PREPARE_FOR_SUBMISSION = "PREPARE_FOR_SUBMISSION"
18
21
  METADATA_REJECTED = "METADATA_REJECTED"
22
+ INVALID_BINARY = "INVALID_BINARY"
19
23
  end
20
24
 
21
25
  module AppStoreAgeRating
@@ -1,5 +1,8 @@
1
1
  require_relative '../model'
2
2
  require_relative '../file_uploader'
3
+ require 'spaceship/globals'
4
+
5
+ require 'digest/md5'
3
6
 
4
7
  module Spaceship
5
8
  class ConnectAPI
@@ -34,39 +37,88 @@ module Spaceship
34
37
  return "appPreviews"
35
38
  end
36
39
 
40
+ def complete?
41
+ (asset_delivery_state || {})["state"] == "COMPLETE"
42
+ end
43
+
37
44
  #
38
45
  # API
39
46
  #
40
47
 
41
- def self.create(app_preview_set_id: nil, path: nil)
48
+ def self.get(app_preview_id: nil)
49
+ Spaceship::ConnectAPI.get_app_preview(app_preview_id: app_preview_id).first
50
+ end
51
+
52
+ # Creates an AppPreview in an AppPreviewSet
53
+ # Setting the optional frame_time_code will force polling until video is done processing
54
+ # @param app_preview_set_id The AppPreviewSet id
55
+ # @param path The path of the file
56
+ # @param frame_time_code The time code for the preview still frame (ex: "00:00:07:01")
57
+ def self.create(app_preview_set_id: nil, path: nil, frame_time_code: nil)
42
58
  require 'faraday'
43
59
 
44
60
  filename = File.basename(path)
45
61
  filesize = File.size(path)
46
- payload = File.binread(path)
62
+ bytes = File.binread(path)
47
63
 
48
64
  post_attributes = {
49
65
  fileSize: filesize,
50
66
  fileName: filename
51
67
  }
52
68
 
53
- post_resp = Spaceship::ConnectAPI.post_app_preview(
69
+ # Create placeholder
70
+ preview = Spaceship::ConnectAPI.post_app_preview(
54
71
  app_preview_set_id: app_preview_set_id,
55
72
  attributes: post_attributes
56
73
  ).to_models.first
57
74
 
58
- upload_operation = post_resp.upload_operations.first
59
- Spaceship::ConnectAPI::FileUploader.upload(upload_operation, payload)
75
+ # Upload the file
76
+ upload_operations = preview.upload_operations
77
+ Spaceship::ConnectAPI::FileUploader.upload(upload_operations, bytes)
60
78
 
79
+ # Update file uploading complete
61
80
  patch_attributes = {
62
81
  uploaded: true,
63
- sourceFileChecksum: "checksum-holder"
82
+ sourceFileChecksum: Digest::MD5.hexdigest(bytes)
64
83
  }
65
84
 
66
- Spaceship::ConnectAPI.patch_app_preview(
67
- app_preview_id: post_resp.id,
68
- attributes: patch_attributes
69
- ).to_models.first
85
+ begin
86
+ preview = Spaceship::ConnectAPI.patch_app_preview(
87
+ app_preview_id: preview.id,
88
+ attributes: patch_attributes
89
+ ).to_models.first
90
+ rescue => error
91
+ puts("Failed to patch app preview. Update may have gone through so verifying") if Spaceship::Globals.verbose?
92
+
93
+ preview = Spaceship::ConnectAPI::AppPreview.get(app_preview_id: preview.id)
94
+ raise error unless preview.complete?
95
+ end
96
+
97
+ # Poll for video processing completion to set still frame time
98
+ unless frame_time_code.nil?
99
+ loop do
100
+ unless preview.video_url.nil?
101
+ puts("Preview processing complete!") if Spaceship::Globals.verbose?
102
+ preview = preview.update(attributes: {
103
+ previewFrameTimeCode: frame_time_code
104
+ })
105
+ puts("Updated preview frame time code!") if Spaceship::Globals.verbose?
106
+ break
107
+ end
108
+
109
+ sleep_time = 30
110
+ puts("Waiting #{sleep_time} seconds before checking status of processing...") if Spaceship::Globals.verbose?
111
+ sleep(sleep_time)
112
+
113
+ preview = Spaceship::ConnectAPI::AppPreview.get(app_preview_id: preview.id)
114
+ end
115
+ end
116
+
117
+ preview
118
+ end
119
+
120
+ def update(attributes: nil)
121
+ Spaceship::ConnectAPI.patch_app_preview(app_preview_id: id, attributes: attributes)
70
122
  end
71
123
 
72
124
  def delete!(filter: {}, includes: nil, limit: nil, sort: nil)
@@ -63,8 +63,8 @@ module Spaceship
63
63
  return resp.to_models
64
64
  end
65
65
 
66
- def upload_preview(path: nil)
67
- return Spaceship::ConnectAPI::AppPreview.create(app_preview_set_id: id, path: path)
66
+ def upload_preview(path: nil, frame_time_code: nil)
67
+ return Spaceship::ConnectAPI::AppPreview.create(app_preview_set_id: id, path: path, frame_time_code: frame_time_code)
68
68
  end
69
69
  end
70
70
  end
@@ -1,4 +1,7 @@
1
1
  require_relative '../model'
2
+ require_relative '../file_uploader'
3
+ require 'digest/md5'
4
+
2
5
  module Spaceship
3
6
  class ConnectAPI
4
7
  class AppReviewAttachment
@@ -31,46 +34,33 @@ module Spaceship
31
34
 
32
35
  filename = File.basename(path)
33
36
  filesize = File.size(path)
34
- payload = File.binread(path)
37
+ bytes = File.binread(path)
35
38
 
36
39
  post_attributes = {
37
40
  fileSize: filesize,
38
41
  fileName: filename
39
42
  }
40
43
 
41
- post_resp = Spaceship::ConnectAPI.post_app_review_attachment(app_store_review_detail_id: app_store_review_detail_id, attributes: post_attributes).to_models.first
42
-
43
- # {
44
- # "method": "PUT",
45
- # "url": "https://some-url-apple-gives-us",
46
- # "length": 57365,
47
- # "offset": 0,
48
- # "requestHeaders": [
49
- # {
50
- # "name": "Content-Type",
51
- # "value": "image/png"
52
- # }
53
- # ]
54
- # }
55
- upload_operation = post_resp.upload_operations.first
56
-
57
- headers = {}
58
- upload_operation["requestHeaders"].each do |hash|
59
- headers[hash["name"]] = hash["value"]
60
- end
44
+ # Create placeholder
45
+ attachment = Spaceship::ConnectAPI.post_app_review_attachment(
46
+ app_store_review_detail_id: app_store_review_detail_id,
47
+ attributes: post_attributes
48
+ ).to_models.first
61
49
 
62
- Faraday.put(
63
- upload_operation["url"],
64
- payload,
65
- headers
66
- )
50
+ # Upload the file
51
+ upload_operations = attachment.upload_operations
52
+ Spaceship::ConnectAPI::FileUploader.upload(upload_operations, bytes)
67
53
 
54
+ # Update file uploading complete
68
55
  patch_attributes = {
69
56
  uploaded: true,
70
- sourceFileChecksum: "checksum-holder"
57
+ sourceFileChecksum: Digest::MD5.hexdigest(bytes)
71
58
  }
72
59
 
73
- Spaceship::ConnectAPI.patch_app_review_attachment(app_review_attachment_id: post_resp.id, attributes: patch_attributes).to_models.first
60
+ Spaceship::ConnectAPI.patch_app_review_attachment(
61
+ app_review_attachment_id: attachment.id,
62
+ attributes: patch_attributes
63
+ ).to_models.first
74
64
  end
75
65
 
76
66
  def delete!(filter: {}, includes: nil, limit: nil, sort: nil)