fastlane 2.155.0 → 2.156.1

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +74 -74
  3. data/deliver/lib/deliver/app_screenshot_iterator.rb +98 -0
  4. data/deliver/lib/deliver/queue_worker.rb +64 -0
  5. data/deliver/lib/deliver/upload_screenshots.rb +123 -125
  6. data/fastlane/lib/fastlane/version.rb +1 -1
  7. data/fastlane/swift/Deliverfile.swift +1 -1
  8. data/fastlane/swift/DeliverfileProtocol.swift +1 -1
  9. data/fastlane/swift/Fastlane.swift +1 -1
  10. data/fastlane/swift/Gymfile.swift +1 -1
  11. data/fastlane/swift/GymfileProtocol.swift +1 -1
  12. data/fastlane/swift/Matchfile.swift +1 -1
  13. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  14. data/fastlane/swift/Precheckfile.swift +1 -1
  15. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  16. data/fastlane/swift/Scanfile.swift +1 -1
  17. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  18. data/fastlane/swift/Screengrabfile.swift +1 -1
  19. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  20. data/fastlane/swift/Snapshotfile.swift +1 -1
  21. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  22. data/fastlane_core/lib/fastlane_core/project.rb +1 -0
  23. data/gym/lib/gym/generators/build_command_generator.rb +0 -1
  24. data/scan/lib/scan/test_command_generator.rb +3 -1
  25. data/screengrab/lib/screengrab/runner.rb +7 -7
  26. data/sigh/lib/sigh/runner.rb +27 -3
  27. data/snapshot/lib/snapshot/test_command_generator_base.rb +3 -1
  28. data/spaceship/lib/spaceship/connect_api/.client.rb.swp +0 -0
  29. data/spaceship/lib/spaceship/connect_api/models/profile.rb +3 -2
  30. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +2 -2
  31. metadata +22 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d05003197423c1e94c2728d49100fbacb7d5f5b657491b94e90f34432d2774ba
4
- data.tar.gz: 99b28cc3d83ec3500d9a90554db8464ee479a7e533141e9246164d26fef8edba
3
+ metadata.gz: 8ddb9f8d09ffcb09f5ddd4d6365f67d41c538e8804630e40911e058a8174aaf2
4
+ data.tar.gz: 8bf0f6513c658bb9891058e5bee6fb22689bc496376e4aff6a2d2e5f1a2b9b74
5
5
  SHA512:
6
- metadata.gz: 9a7b6ce2df7348ad80f366ad54a619ad1d966348e3e2a0f616f43d160681c1ba15625f7c16923d4b0bbfa3c873b3936891001f53c5349abba1e0a3c0a6bb4704
7
- data.tar.gz: c75739db5b1e8dcea81230f67effe0d1b46de274cab20d85183aa3dafb64088ed85a7260f2aa47e14e802c8a81879b541d7ba4fbca55f56a2c84c1cb09fbeae0
6
+ metadata.gz: 9627a6de633cc011c1cd083468869543125e4eb021f025152c3aaf0e4e6067c80bcc514e71c1d425485138df0ff7ab43fa70771d846e0270c49ad50b60dc47c1
7
+ data.tar.gz: 177ba0af8b688d96e5374f7a3175663cebe4e3bae3966733880e75e0634cc4e1cdedc3f12c1c8ac0cd415b024eb3aa9385489d67f98b2e9d4e5175631d2874bf
data/README.md CHANGED
@@ -34,43 +34,17 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
34
34
  <!-- This table is regenerated and resorted on each release -->
35
35
  <table id='team'>
36
36
  <tr>
37
- <td id='danielle-tomlinson'>
38
- <a href='https://github.com/endocrimes'>
39
- <img src='https://github.com/endocrimes.png?size=140'>
40
- </a>
41
- <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
42
- </td>
43
- <td id='olivier-halligon'>
44
- <a href='https://github.com/AliSoftware'>
45
- <img src='https://github.com/AliSoftware.png?size=140'>
46
- </a>
47
- <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
48
- </td>
49
- <td id='max-ott'>
50
- <a href='https://github.com/max-ott'>
51
- <img src='https://github.com/max-ott.png?size=140'>
52
- </a>
53
- <h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
54
- </td>
55
37
  <td id='jorge-revuelta-h'>
56
38
  <a href='https://github.com/minuscorp'>
57
39
  <img src='https://github.com/minuscorp.png?size=140'>
58
40
  </a>
59
41
  <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
60
42
  </td>
61
- <td id='andrew-mcburney'>
62
- <a href='https://github.com/armcburney'>
63
- <img src='https://github.com/armcburney.png?size=140'>
64
- </a>
65
- <h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
66
- </td>
67
- </tr>
68
- <tr>
69
- <td id='jérôme-lacoste'>
70
- <a href='https://github.com/lacostej'>
71
- <img src='https://github.com/lacostej.png?size=140'>
43
+ <td id='luka-mirosevic'>
44
+ <a href='https://github.com/lmirosevic'>
45
+ <img src='https://github.com/lmirosevic.png?size=140'>
72
46
  </a>
73
- <h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
47
+ <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
74
48
  </td>
75
49
  <td id='josh-holtz'>
76
50
  <a href='https://github.com/joshdholtz'>
@@ -78,11 +52,11 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
78
52
  </a>
79
53
  <h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
80
54
  </td>
81
- <td id='manu-wallner'>
82
- <a href='https://github.com/milch'>
83
- <img src='https://github.com/milch.png?size=140'>
55
+ <td id='felix-krause'>
56
+ <a href='https://github.com/KrauseFx'>
57
+ <img src='https://github.com/KrauseFx.png?size=140'>
84
58
  </a>
85
- <h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
59
+ <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
86
60
  </td>
87
61
  <td id='fumiya-nakamura'>
88
62
  <a href='https://github.com/nafu'>
@@ -90,57 +64,51 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
90
64
  </a>
91
65
  <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
92
66
  </td>
93
- <td id='felix-krause'>
94
- <a href='https://github.com/KrauseFx'>
95
- <img src='https://github.com/KrauseFx.png?size=140'>
96
- </a>
97
- <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
98
- </td>
99
67
  </tr>
100
68
  <tr>
69
+ <td id='max-ott'>
70
+ <a href='https://github.com/max-ott'>
71
+ <img src='https://github.com/max-ott.png?size=140'>
72
+ </a>
73
+ <h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
74
+ </td>
101
75
  <td id='stefan-natchev'>
102
76
  <a href='https://github.com/snatchev'>
103
77
  <img src='https://github.com/snatchev.png?size=140'>
104
78
  </a>
105
79
  <h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
106
80
  </td>
107
- <td id='matthew-ellis'>
108
- <a href='https://github.com/matthewellis'>
109
- <img src='https://github.com/matthewellis.png?size=140'>
110
- </a>
111
- <h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
112
- </td>
113
- <td id='daniel-jankowski'>
114
- <a href='https://github.com/mollyIV'>
115
- <img src='https://github.com/mollyIV.png?size=140'>
81
+ <td id='jérôme-lacoste'>
82
+ <a href='https://github.com/lacostej'>
83
+ <img src='https://github.com/lacostej.png?size=140'>
116
84
  </a>
117
- <h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
85
+ <h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
118
86
  </td>
119
- <td id='jimmy-dee'>
120
- <a href='https://github.com/jdee'>
121
- <img src='https://github.com/jdee.png?size=140'>
87
+ <td id='joshua-liebowitz'>
88
+ <a href='https://github.com/taquitos'>
89
+ <img src='https://github.com/taquitos.png?size=140'>
122
90
  </a>
123
- <h4 align='center'>Jimmy Dee</h4>
91
+ <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
124
92
  </td>
125
- <td id='jan-piotrowski'>
126
- <a href='https://github.com/janpio'>
127
- <img src='https://github.com/janpio.png?size=140'>
93
+ <td id='iulian-onofrei'>
94
+ <a href='https://github.com/revolter'>
95
+ <img src='https://github.com/revolter.png?size=140'>
128
96
  </a>
129
- <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
97
+ <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
130
98
  </td>
131
99
  </tr>
132
100
  <tr>
133
- <td id='luka-mirosevic'>
134
- <a href='https://github.com/lmirosevic'>
135
- <img src='https://github.com/lmirosevic.png?size=140'>
101
+ <td id='jimmy-dee'>
102
+ <a href='https://github.com/jdee'>
103
+ <img src='https://github.com/jdee.png?size=140'>
136
104
  </a>
137
- <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
105
+ <h4 align='center'>Jimmy Dee</h4>
138
106
  </td>
139
- <td id='maksym-grebenets'>
140
- <a href='https://github.com/mgrebenets'>
141
- <img src='https://github.com/mgrebenets.png?size=140'>
107
+ <td id='andrew-mcburney'>
108
+ <a href='https://github.com/armcburney'>
109
+ <img src='https://github.com/armcburney.png?size=140'>
142
110
  </a>
143
- <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
111
+ <h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
144
112
  </td>
145
113
  <td id='kohki-miki'>
146
114
  <a href='https://github.com/giginet'>
@@ -148,31 +116,63 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
148
116
  </a>
149
117
  <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
150
118
  </td>
119
+ <td id='jan-piotrowski'>
120
+ <a href='https://github.com/janpio'>
121
+ <img src='https://github.com/janpio.png?size=140'>
122
+ </a>
123
+ <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
124
+ </td>
125
+ <td id='olivier-halligon'>
126
+ <a href='https://github.com/AliSoftware'>
127
+ <img src='https://github.com/AliSoftware.png?size=140'>
128
+ </a>
129
+ <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
130
+ </td>
131
+ </tr>
132
+ <tr>
151
133
  <td id='helmut-januschka'>
152
134
  <a href='https://github.com/hjanuschka'>
153
135
  <img src='https://github.com/hjanuschka.png?size=140'>
154
136
  </a>
155
137
  <h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
156
138
  </td>
139
+ <td id='daniel-jankowski'>
140
+ <a href='https://github.com/mollyIV'>
141
+ <img src='https://github.com/mollyIV.png?size=140'>
142
+ </a>
143
+ <h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
144
+ </td>
145
+ <td id='manu-wallner'>
146
+ <a href='https://github.com/milch'>
147
+ <img src='https://github.com/milch.png?size=140'>
148
+ </a>
149
+ <h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
150
+ </td>
157
151
  <td id='aaron-brager'>
158
152
  <a href='https://github.com/getaaron'>
159
153
  <img src='https://github.com/getaaron.png?size=140'>
160
154
  </a>
161
155
  <h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
162
156
  </td>
157
+ <td id='matthew-ellis'>
158
+ <a href='https://github.com/matthewellis'>
159
+ <img src='https://github.com/matthewellis.png?size=140'>
160
+ </a>
161
+ <h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
162
+ </td>
163
163
  </tr>
164
164
  <tr>
165
- <td id='iulian-onofrei'>
166
- <a href='https://github.com/revolter'>
167
- <img src='https://github.com/revolter.png?size=140'>
165
+ <td id='maksym-grebenets'>
166
+ <a href='https://github.com/mgrebenets'>
167
+ <img src='https://github.com/mgrebenets.png?size=140'>
168
168
  </a>
169
- <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
169
+ <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
170
170
  </td>
171
- <td id='joshua-liebowitz'>
172
- <a href='https://github.com/taquitos'>
173
- <img src='https://github.com/taquitos.png?size=140'>
171
+ <td id='danielle-tomlinson'>
172
+ <a href='https://github.com/endocrimes'>
173
+ <img src='https://github.com/endocrimes.png?size=140'>
174
174
  </a>
175
- <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
175
+ <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
176
176
  </td>
177
177
  </table>
178
178
 
@@ -0,0 +1,98 @@
1
+ module Deliver
2
+ # This is a convinient class that enumerates app store connect's screenshots in various degrees.
3
+ class AppScreenshotIterator
4
+ NUMBER_OF_THREADS = Helper.test? ? 1 : [ENV.fetch("DELIVER_NUMBER_OF_THREADS", 10).to_i, 10].min
5
+
6
+ # @param localizations [Array<Spaceship::ConnectAPI::AppStoreVersionLocalization>]
7
+ def initialize(localizations)
8
+ @localizations = localizations
9
+ end
10
+
11
+ # Iterate app_screenshot_set over localizations
12
+ #
13
+ # @yield [localization, app_screenshot_set]
14
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreVersionLocalization] localization
15
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreScreenshotSet] app_screenshot_set
16
+ def each_app_screenshot_set(&block)
17
+ return enum_for(__method__) unless block_given?
18
+
19
+ # Collect app_screenshot_sets from localizations in parallel but
20
+ # limit the number of threads working at a time with using `lazy` and `force` controls
21
+ # to not attack App Store Connect
22
+ results = @localizations.each_slice(NUMBER_OF_THREADS).lazy.map do |localizations|
23
+ localizations.map do |localization|
24
+ Thread.new do
25
+ [localization, localization.get_app_screenshot_sets]
26
+ end
27
+ end
28
+ end.flat_map do |threads|
29
+ threads.map { |t| t.join.value }
30
+ end.force
31
+
32
+ results.each do |localization, app_screenshot_sets|
33
+ app_screenshot_sets.each do |app_screenshot_set|
34
+ yield(localization, app_screenshot_set)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Iterate app_screenshot over localizations and app_screenshot_sets
40
+ #
41
+ # @yield [localization, app_screenshot_set, app_screenshot]
42
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreVersionLocalization] localization
43
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreScreenshotSet] app_screenshot_set
44
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreScreenshot] app_screenshot
45
+ def each_app_screenshot(&block)
46
+ return enum_for(__method__) unless block_given?
47
+
48
+ each_app_screenshot_set do |localization, app_screenshot_set|
49
+ app_screenshot_set.app_screenshots.each do |app_screenshot|
50
+ yield(localization, app_screenshot_set, app_screenshot)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Iterate given local app_screenshot over localizations and app_screenshot_sets with index within each app_screenshot_set
56
+ #
57
+ # @param screenshots_per_language [Hash<String, Array<Deliver::AppScreenshot>]
58
+ # @yield [localization, app_screenshot_set, app_screenshot, index]
59
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreVersionLocalization] localization
60
+ # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreScreenshotSet] app_screenshot_set
61
+ # @yieldparam [optional, Deliver::AppScreenshot] screenshot
62
+ # @yieldparam [optional, Integer] index a number reperesents which position the screenshot will be
63
+ def each_local_screenshot(screenshots_per_language, &block)
64
+ return enum_for(__method__, screenshots_per_language) unless block_given?
65
+
66
+ # Iterate over all the screenshots per language and display_type
67
+ # and then enqueue them to worker one by one if it's not duplciated on App Store Connect
68
+ screenshots_per_language.map do |language, screenshots_for_language|
69
+ localization = @localizations.find { |l| l.locale == language }
70
+ [localization, screenshots_for_language]
71
+ end.reject do |localization, _|
72
+ localization.nil?
73
+ end.each do |localization, screenshots_for_language|
74
+ iterate_over_screenshots_per_language(localization, screenshots_for_language, &block)
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def iterate_over_screenshots_per_language(localization, screenshots_for_language, &block)
81
+ app_screenshot_sets_per_display_type = localization.get_app_screenshot_sets.map { |set| [set.screenshot_display_type, set] }.to_h
82
+ screenshots_per_display_type = screenshots_for_language.reject { |screenshot| screenshot.device_type.nil? }.group_by(&:device_type)
83
+
84
+ screenshots_per_display_type.each do |display_type, screenshots|
85
+ # Create AppScreenshotSet for given display_type if it doesn't exsit
86
+ app_screenshot_set = app_screenshot_sets_per_display_type[display_type]
87
+ app_screenshot_set ||= localization.create_app_screenshot_set(attributes: { screenshotDisplayType: display_type })
88
+ iterate_over_screenshots_per_display_type(localization, app_screenshot_set, screenshots, &block)
89
+ end
90
+ end
91
+
92
+ def iterate_over_screenshots_per_display_type(localization, app_screenshot_set, screenshots, &block)
93
+ screenshots.each.with_index do |screenshot, index|
94
+ yield(localization, app_screenshot_set, screenshot, index)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,64 @@
1
+ require 'thread'
2
+
3
+ module Deliver
4
+ # This dispatches jobs to worker threads and make it work in parallel.
5
+ # It's suitable for I/O bounds works and not for CPU bounds works.
6
+ # Use this when you have all the items that you'll process in advance.
7
+ # Simply enqueue them to this and call `QueueWorker#start`.
8
+ class QueueWorker
9
+ # @param concurrency (Numeric) - A number of threads to be created
10
+ # @param block (Proc) - A task you want to execute with enqueued items
11
+ def initialize(concurrency, &block)
12
+ @concurrency = concurrency
13
+ @block = block
14
+ @queue = Queue.new
15
+ end
16
+
17
+ # @param job (Object) - An arbitary object that keeps parameters
18
+ def enqueue(job)
19
+ @queue.push(job)
20
+ end
21
+
22
+ # Call this after you enqueuned all the jobs you want to process
23
+ # This method blocks current thread until all the enqueued jobs are processed
24
+ def start
25
+ threads = []
26
+ @concurrency.times do
27
+ threads << Thread.new do
28
+ while running? && !empty?
29
+ job = @queue.pop
30
+ @block.call(job) if job
31
+ end
32
+ end
33
+ end
34
+
35
+ wait_for_complete
36
+ threads.each(&:join)
37
+ end
38
+
39
+ private
40
+
41
+ def running?
42
+ !@queue.closed?
43
+ end
44
+
45
+ def empty?
46
+ @queue.empty?
47
+ end
48
+
49
+ def wait_for_complete
50
+ wait_thread = Thread.new do
51
+ loop do
52
+ if @queue.empty?
53
+ @queue.close
54
+ break
55
+ end
56
+
57
+ sleep(1)
58
+ end
59
+ end
60
+
61
+ wait_thread.join
62
+ end
63
+ end
64
+ end
@@ -4,10 +4,17 @@ require 'digest/md5'
4
4
  require_relative 'app_screenshot'
5
5
  require_relative 'module'
6
6
  require_relative 'loader'
7
+ require_relative 'queue_worker'
8
+ require_relative 'app_screenshot_iterator'
7
9
 
8
10
  module Deliver
9
11
  # upload screenshots to App Store Connect
10
12
  class UploadScreenshots
13
+ DeleteScreenshotJob = Struct.new(:app_screenshot, :localization, :app_screenshot_set)
14
+ UploadScreenshotJob = Struct.new(:app_screenshot_set, :path)
15
+
16
+ NUMBER_OF_THREADS = Helper.test? ? 1 : [ENV.fetch("DELIVER_NUMBER_OF_THREADS", 10).to_i, 10].min
17
+
11
18
  def upload(options, screenshots)
12
19
  return if options[:skip_screenshots]
13
20
  return if options[:edit_live]
@@ -50,57 +57,47 @@ module Deliver
50
57
  localizations = version.get_app_store_version_localizations
51
58
  end
52
59
 
53
- upload_screenshots(screenshots_per_language, localizations, options)
60
+ upload_screenshots(localizations, screenshots_per_language)
61
+
62
+ Helper.show_loading_indicator("Sorting screenshots uploaded...")
63
+ sort_screenshots(localizations)
64
+ Helper.hide_loading_indicator
65
+
66
+ UI.success("Successfully uploaded screenshots to App Store Connect")
54
67
  end
55
68
 
56
69
  def delete_screenshots(localizations, screenshots_per_language, tries: 5)
57
70
  tries -= 1
58
71
 
59
- # Get localizations on version
60
- localizations.each do |localization|
61
- # Only delete screenshots if trying to upload
62
- next unless screenshots_per_language.keys.include?(localization.locale)
63
-
64
- # Iterate over all screenshots for each set and delete
65
- screenshot_sets = localization.get_app_screenshot_sets
66
-
67
- # Multi threading delete on single localization
68
- threads = []
69
- errors = []
70
-
71
- screenshot_sets.each do |screenshot_set|
72
- UI.message("Removing all previously uploaded screenshots for '#{localization.locale}' '#{screenshot_set.screenshot_display_type}'...")
73
- screenshot_set.app_screenshots.each do |screenshot|
74
- UI.verbose("Deleting screenshot - #{localization.locale} #{screenshot_set.screenshot_display_type} #{screenshot.id}")
75
- threads << Thread.new do
76
- begin
77
- screenshot.delete!
78
- UI.verbose("Deleted screenshot - #{localization.locale} #{screenshot_set.screenshot_display_type} #{screenshot.id}")
79
- rescue => error
80
- UI.verbose("Failed to delete screenshot - #{localization.locale} #{screenshot_set.screenshot_display_type} #{screenshot.id}")
81
- errors << error
82
- end
83
- end
84
- end
72
+ worker = QueueWorker.new(NUMBER_OF_THREADS) do |job|
73
+ start_time = Time.now
74
+ target = "#{job.localization.locale} #{job.app_screenshot_set.screenshot_display_type} #{job.app_screenshot.id}"
75
+ begin
76
+ UI.verbose("Deleting '#{target}'")
77
+ job.app_screenshot.delete!
78
+ UI.message("Deleted '#{target}' - (#{Time.now - start_time} secs)")
79
+ rescue => error
80
+ UI.error("Failed to delete screenshot #{target} - (#{Time.now - start_time} secs)")
81
+ UI.error(error.message)
85
82
  end
83
+ end
86
84
 
87
- sleep(1) # Feels bad but sleeping a bit to let the threads catchup
88
-
89
- unless threads.empty?
90
- Helper.show_loading_indicator("Waiting for screenshots to be deleted for '#{localization.locale}'... (might be slow)") unless FastlaneCore::Globals.verbose?
91
- threads.each(&:join)
92
- Helper.hide_loading_indicator unless FastlaneCore::Globals.verbose?
93
- end
85
+ iterator = AppScreenshotIterator.new(localizations)
86
+ iterator.each_app_screenshot do |localization, app_screenshot_set, app_screenshot|
87
+ # Only delete screenshots if trying to upload
88
+ next unless screenshots_per_language.keys.include?(localization.locale)
94
89
 
95
- # Crash if any errors happen while deleting
96
- errors.each do |error|
97
- UI.error(error.message)
98
- end
90
+ UI.verbose("Queued delete sceeenshot job for #{localization.locale} #{app_screenshot_set.screenshot_display_type} #{app_screenshot.id}")
91
+ worker.enqueue(DeleteScreenshotJob.new(app_screenshot, localization, app_screenshot_set))
99
92
  end
100
93
 
94
+ worker.start
95
+
101
96
  # Verify all screenshots have been deleted
102
97
  # Sometimes API requests will fail but screenshots will still be deleted
103
- count = count_screenshots(localizations)
98
+ count = iterator.each_app_screenshot_set.map { |_, app_screenshot_set| app_screenshot_set }
99
+ .reduce(0) { |sum, app_screenshot_set| sum + app_screenshot_set.app_screenshots.size }
100
+
104
101
  UI.important("Number of screenshots not deleted: #{count}")
105
102
  if count > 0
106
103
  if tries.zero?
@@ -114,113 +111,108 @@ module Deliver
114
111
  end
115
112
  end
116
113
 
117
- def count_screenshots(localizations)
118
- count = 0
119
- localizations.each do |localization|
120
- screenshot_sets = localization.get_app_screenshot_sets
121
- screenshot_sets.each do |screenshot_set|
122
- count += screenshot_set.app_screenshots.size
123
- end
124
- end
125
-
126
- return count
127
- end
128
-
129
- def upload_screenshots(screenshots_per_language, localizations, options)
130
- # Check if should wait for processing
131
- # Default to waiting if submitting for review (since needed for submission)
132
- # Otherwise use enviroment variable
133
- if ENV["DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING"].nil?
134
- wait_for_processing = options[:submit_for_review]
135
- UI.verbose("Setting wait_for_processing from ':submit_for_review' option")
136
- else
137
- UI.verbose("Setting wait_for_processing from 'DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING' environment variable")
138
- wait_for_processing = !FastlaneCore::Env.truthy?("DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING")
139
- end
140
-
141
- if wait_for_processing
142
- UI.important("Will wait for screenshot image processing")
143
- UI.important("Set env DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING=true to skip waiting for screenshots to process")
144
- else
145
- UI.important("Skipping the wait for screenshot image processing (which may affect submission)")
146
- UI.important("Set env DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING=false to wait for screenshots to process")
147
- end
114
+ def upload_screenshots(localizations, screenshots_per_language, tries: 5)
115
+ tries -= 1
148
116
 
149
117
  # Upload screenshots
150
- indized = {} # per language and device type
151
-
152
- screenshots_per_language.each do |language, screenshots_for_language|
153
- # Find localization to upload screenshots to
154
- localization = localizations.find do |l|
155
- l.locale == language
118
+ worker = QueueWorker.new(NUMBER_OF_THREADS) do |job|
119
+ begin
120
+ UI.verbose("Uploading '#{job.path}'...")
121
+ start_time = Time.now
122
+ job.app_screenshot_set.upload_screenshot(path: job.path, wait_for_processing: false)
123
+ UI.message("Uploaded '#{job.path}'... (#{Time.now - start_time} secs)")
124
+ rescue => error
125
+ UI.error(error)
156
126
  end
127
+ end
157
128
 
158
- unless localization
159
- UI.error("Couldn't find localization on version for #{language}")
129
+ number_of_screenshots = 0
130
+ iterator = AppScreenshotIterator.new(localizations)
131
+ iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot, index|
132
+ if index >= 10
133
+ UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})")
160
134
  next
161
135
  end
162
136
 
163
- indized[localization.locale] ||= {}
137
+ checksum = UploadScreenshots.calculate_checksum(screenshot.path)
138
+ duplicate = (app_screenshot_set.app_screenshots || []).any? { |s| s.source_file_checksum == checksum }
164
139
 
165
- # Create map to find screenshot set to add screenshot to
166
- app_screenshot_sets_map = {}
167
- app_screenshot_sets = localization.get_app_screenshot_sets
168
- app_screenshot_sets.each do |app_screenshot_set|
169
- app_screenshot_sets_map[app_screenshot_set.screenshot_display_type] = app_screenshot_set
140
+ # Enqueue uploading job if it's not duplicated otherwise screenshot will be skipped
141
+ if duplicate
142
+ UI.message("Previous uploaded. Skipping '#{screenshot.path}'...")
143
+ else
144
+ worker.enqueue(UploadScreenshotJob.new(app_screenshot_set, screenshot.path))
145
+ end
170
146
 
171
- # Set initial screnshot count
172
- indized[localization.locale][app_screenshot_set.screenshot_display_type] ||= {
173
- count: app_screenshot_set.app_screenshots.size,
174
- checksums: []
175
- }
147
+ number_of_screenshots += 1
148
+ end
176
149
 
177
- checksums = app_screenshot_set.app_screenshots.map(&:source_file_checksum).uniq
178
- indized[localization.locale][app_screenshot_set.screenshot_display_type][:checksums] = checksums
179
- end
150
+ worker.start
180
151
 
181
- UI.message("Uploading #{screenshots_for_language.length} screenshots for language #{language}")
182
- screenshots_for_language.each do |screenshot|
183
- display_type = screenshot.device_type
184
- set = app_screenshot_sets_map[display_type]
152
+ UI.verbose('Uploading jobs are completed')
185
153
 
186
- if display_type.nil?
187
- UI.error("Error... Screenshot size #{screenshot.screen_size} not valid for App Store Connect")
188
- next
189
- end
154
+ Helper.show_loading_indicator("Waiting for all the screenshots processed...")
155
+ states = wait_for_complete(iterator)
156
+ Helper.hide_loading_indicator
157
+ retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, localizations, screenshots_per_language)
190
158
 
191
- unless set
192
- set = localization.create_app_screenshot_set(attributes: {
193
- screenshotDisplayType: display_type
194
- })
195
- app_screenshot_sets_map[display_type] = set
159
+ UI.message("Successfully uploaded all screenshots")
160
+ end
196
161
 
197
- indized[localization.locale][set.screenshot_display_type] = {
198
- count: 0,
199
- checksums: []
200
- }
201
- end
162
+ # Verify all screenshots have been processed
163
+ def wait_for_complete(iterator)
164
+ loop do
165
+ states = iterator.each_app_screenshot.map { |_, _, app_screenshot| app_screenshot }.each_with_object({}) do |app_screenshot, hash|
166
+ state = app_screenshot.asset_delivery_state['state']
167
+ hash[state] ||= 0
168
+ hash[state] += 1
169
+ end
202
170
 
203
- index = indized[localization.locale][set.screenshot_display_type][:count]
171
+ is_processing = states.fetch('UPLOAD_COMPLETE', 0) > 0
172
+ return states unless is_processing
204
173
 
205
- if index >= 10
206
- UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})")
207
- next
208
- end
174
+ UI.verbose("There are still incomplete screenshots - #{states}")
175
+ sleep(5)
176
+ end
177
+ end
209
178
 
210
- bytes = File.binread(screenshot.path)
211
- checksum = Digest::MD5.hexdigest(bytes)
212
- duplicate = indized[localization.locale][set.screenshot_display_type][:checksums].include?(checksum)
179
+ # Verify all screenshots states on App Store Connect are okay
180
+ def retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, localizations, screenshots_per_language)
181
+ is_failure = states.fetch("FAILED", 0) > 0
182
+ is_missing_screenshot = states.reduce(0) { |sum, (k, v)| sum + v } != number_of_screenshots && !screenshots_per_language.empty?
213
183
 
214
- if duplicate
215
- UI.message("Previous uploaded. Skipping '#{screenshot.path}'...")
216
- else
217
- indized[localization.locale][set.screenshot_display_type][:count] += 1
218
- UI.message("Uploading '#{screenshot.path}'...")
219
- set.upload_screenshot(path: screenshot.path, wait_for_processing: wait_for_processing)
184
+ if is_failure || is_missing_screenshot
185
+ if tries.zero?
186
+ incomplete_screenshot_count = states.reject { |k, v| k == 'COMPLETE' }.reduce(0) { |sum, (k, v)| sum + v }
187
+ UI.user_error!("Failed verification of all screenshots uploaded... #{incomplete_screenshot_count} incomplete screenshot(s) still exist")
188
+ else
189
+ UI.error("Failed to upload all screenshots... Tries remaining: #{tries}")
190
+ # Delete bad entries before retry
191
+ iterator.each_app_screenshot do |_, _, app_screenshot|
192
+ app_screenshot.delete! unless app_screenshot.complete?
220
193
  end
194
+ upload_screenshots(localizations, screenshots_per_language, tries: tries)
221
195
  end
222
196
  end
223
- UI.success("Successfully uploaded screenshots to App Store Connect")
197
+ end
198
+
199
+ def sort_screenshots(localizations)
200
+ iterator = AppScreenshotIterator.new(localizations)
201
+
202
+ # Re-order screenshots within app_screenshot_set
203
+ worker = QueueWorker.new(NUMBER_OF_THREADS) do |app_screenshot_set|
204
+ original_ids = app_screenshot_set.app_screenshots.map(&:id)
205
+ sorted_ids = app_screenshot_set.app_screenshots.sort_by(&:file_name).map(&:id)
206
+ if original_ids != sorted_ids
207
+ app_screenshot_set.reorder_screenshots(app_screenshot_ids: sorted_ids)
208
+ end
209
+ end
210
+
211
+ iterator.each_app_screenshot_set do |_, app_screenshot_set|
212
+ worker.enqueue(app_screenshot_set)
213
+ end
214
+
215
+ worker.start
224
216
  end
225
217
 
226
218
  def collect_screenshots(options)
@@ -299,5 +291,11 @@ module Deliver
299
291
  Spaceship::Tunes.client.available_languages
300
292
  end
301
293
  end
294
+
295
+ # helper method to mock this step in tests
296
+ def self.calculate_checksum(path)
297
+ bytes = File.binread(path)
298
+ Digest::MD5.hexdigest(bytes)
299
+ end
302
300
  end
303
301
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
- VERSION = '2.155.0'.freeze
2
+ VERSION = '2.156.1'.freeze
3
3
  DESCRIPTION = "The easiest way to automate beta deployments and releases for your iOS and Android apps".freeze
4
4
  MINIMUM_XCODE_RELEASE = "7.0".freeze
5
5
  RUBOCOP_REQUIREMENT = '0.49.1'.freeze
@@ -14,4 +14,4 @@ class Deliverfile: DeliverfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -245,4 +245,4 @@ extension DeliverfileProtocol {
245
245
 
246
246
  // Please don't remove the lines below
247
247
  // They are used to detect outdated files
248
- // FastlaneRunnerAPIVersion [0.9.29]
248
+ // FastlaneRunnerAPIVersion [0.9.34]
@@ -9204,4 +9204,4 @@ let snapshotfile: Snapshotfile = Snapshotfile()
9204
9204
 
9205
9205
  // Please don't remove the lines below
9206
9206
  // They are used to detect outdated files
9207
- // FastlaneRunnerAPIVersion [0.9.82]
9207
+ // FastlaneRunnerAPIVersion [0.9.87]
@@ -14,4 +14,4 @@ class Gymfile: GymfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -181,4 +181,4 @@ extension GymfileProtocol {
181
181
 
182
182
  // Please don't remove the lines below
183
183
  // They are used to detect outdated files
184
- // FastlaneRunnerAPIVersion [0.9.32]
184
+ // FastlaneRunnerAPIVersion [0.9.37]
@@ -14,4 +14,4 @@ class Matchfile: MatchfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -165,4 +165,4 @@ extension MatchfileProtocol {
165
165
 
166
166
  // Please don't remove the lines below
167
167
  // They are used to detect outdated files
168
- // FastlaneRunnerAPIVersion [0.9.26]
168
+ // FastlaneRunnerAPIVersion [0.9.31]
@@ -14,4 +14,4 @@ class Precheckfile: PrecheckfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -33,4 +33,4 @@ extension PrecheckfileProtocol {
33
33
 
34
34
  // Please don't remove the lines below
35
35
  // They are used to detect outdated files
36
- // FastlaneRunnerAPIVersion [0.9.25]
36
+ // FastlaneRunnerAPIVersion [0.9.30]
@@ -14,4 +14,4 @@ class Scanfile: ScanfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -257,4 +257,4 @@ extension ScanfileProtocol {
257
257
 
258
258
  // Please don't remove the lines below
259
259
  // They are used to detect outdated files
260
- // FastlaneRunnerAPIVersion [0.9.37]
260
+ // FastlaneRunnerAPIVersion [0.9.42]
@@ -14,4 +14,4 @@ class Screengrabfile: ScreengrabfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -93,4 +93,4 @@ extension ScreengrabfileProtocol {
93
93
 
94
94
  // Please don't remove the lines below
95
95
  // They are used to detect outdated files
96
- // FastlaneRunnerAPIVersion [0.9.27]
96
+ // FastlaneRunnerAPIVersion [0.9.32]
@@ -14,4 +14,4 @@ class Snapshotfile: SnapshotfileProtocol {
14
14
  // during the `init` process, and you won't see this message
15
15
  }
16
16
 
17
- // Generated with fastlane 2.155.0
17
+ // Generated with fastlane 2.156.1
@@ -181,4 +181,4 @@ extension SnapshotfileProtocol {
181
181
 
182
182
  // Please don't remove the lines below
183
183
  // They are used to detect outdated files
184
- // FastlaneRunnerAPIVersion [0.9.21]
184
+ // FastlaneRunnerAPIVersion [0.9.26]
@@ -320,6 +320,7 @@ module FastlaneCore
320
320
  proj << "-scheme #{options[:scheme].shellescape}" if options[:scheme]
321
321
  proj << "-project #{options[:project].shellescape}" if options[:project]
322
322
  proj << "-configuration #{options[:configuration].shellescape}" if options[:configuration]
323
+ proj << "-derivedDataPath #{options[:derived_data_path].shellescape}" if options[:derived_data_path]
323
324
  proj << "-xcconfig #{options[:xcconfig].shellescape}" if options[:xcconfig]
324
325
 
325
326
  if FastlaneCore::Helper.xcode_at_least?('11.0') && options[:cloned_source_packages_path]
@@ -38,7 +38,6 @@ module Gym
38
38
  options << "-toolchain '#{config[:toolchain]}'" if config[:toolchain]
39
39
  options << "-destination '#{config[:destination]}'" if config[:destination]
40
40
  options << "-archivePath #{archive_path.shellescape}" unless config[:skip_archive]
41
- options << "-derivedDataPath '#{config[:derived_data_path]}'" if config[:derived_data_path]
42
41
  options << "-resultBundlePath '#{result_bundle_path}'" if config[:result_bundle]
43
42
  options << config[:xcargs] if config[:xcargs]
44
43
  options << "OTHER_SWIFT_FLAGS=\"-Xfrontend -debug-time-function-bodies\"" if config[:analyze_build_time]
@@ -35,7 +35,9 @@ module Scan
35
35
  options << "-sdk '#{config[:sdk]}'" if config[:sdk]
36
36
  options << destination # generated in `detect_values`
37
37
  options << "-toolchain '#{config[:toolchain]}'" if config[:toolchain]
38
- options << "-derivedDataPath '#{config[:derived_data_path]}'" if config[:derived_data_path]
38
+ if config[:derived_data_path] && !options.include?("-derivedDataPath #{config[:derived_data_path].shellescape}")
39
+ options << "-derivedDataPath #{config[:derived_data_path].shellescape}"
40
+ end
39
41
  options << "-resultBundlePath '#{result_bundle_path}'" if config[:result_bundle]
40
42
  if FastlaneCore::Helper.xcode_at_least?(10)
41
43
  options << "-parallel-testing-worker-count #{config[:concurrent_workers]}" if config[:concurrent_workers]
@@ -66,7 +66,7 @@ module Screengrab
66
66
  run_adb_command("-s #{device_serial} root", print_all: false, print_command: true)
67
67
  end
68
68
 
69
- clear_device_previous_screenshots(device_serial, device_screenshots_paths)
69
+ clear_device_previous_screenshots(@config[:app_package_name], device_serial, device_screenshots_paths)
70
70
 
71
71
  app_apk_path ||= select_app_apk(discovered_apk_paths)
72
72
  tests_apk_path ||= select_tests_apk(discovered_apk_paths)
@@ -157,12 +157,12 @@ module Screengrab
157
157
  end.flatten
158
158
  end
159
159
 
160
- def clear_device_previous_screenshots(device_serial, device_screenshots_paths)
160
+ def clear_device_previous_screenshots(app_package_name, device_serial, device_screenshots_paths)
161
161
  UI.message('Cleaning screenshots on device')
162
162
 
163
163
  device_screenshots_paths.each do |device_path|
164
- if_device_path_exists(device_serial, device_path) do |path|
165
- run_adb_command("-s #{device_serial} shell rm -rf #{path}",
164
+ if_device_path_exists(app_package_name, device_serial, device_path) do |path|
165
+ run_adb_command("-s #{device_serial} shell run-as #{app_package_name} rm -rf #{path}",
166
166
  print_all: true,
167
167
  print_command: true)
168
168
  end
@@ -296,7 +296,7 @@ module Screengrab
296
296
 
297
297
  Dir.mktmpdir do |tempdir|
298
298
  device_screenshots_paths.each do |device_path|
299
- if_device_path_exists(device_serial, device_path) do |path|
299
+ if_device_path_exists(@config[:app_package_name], device_serial, device_path) do |path|
300
300
  next unless path.include?(locale)
301
301
  run_adb_command("-s #{device_serial} pull #{path} #{tempdir}",
302
302
  print_all: false,
@@ -361,8 +361,8 @@ module Screengrab
361
361
 
362
362
  # Some device commands fail if executed against a device path that does not exist, so this helper method
363
363
  # provides a way to conditionally execute a block only if the provided path exists on the device.
364
- def if_device_path_exists(device_serial, device_path)
365
- return if run_adb_command("-s #{device_serial} shell ls #{device_path}",
364
+ def if_device_path_exists(app_package_name, device_serial, device_path)
365
+ return if run_adb_command("-s #{device_serial} shell run-as #{app_package_name} ls #{device_path}",
366
366
  print_all: false,
367
367
  print_command: false).include?('No such file')
368
368
 
@@ -171,7 +171,9 @@ module Sigh
171
171
  name: name,
172
172
  profile_type: profile_type,
173
173
  bundle_id_id: bundle_id.id,
174
- certificate_ids: [certificate_to_use.id]
174
+ certificate_ids: certificates_to_use.map(&:id),
175
+ device_ids: devices_to_use.map(&:id),
176
+ template_name: Sigh.config[:template_name]
175
177
  )
176
178
 
177
179
  profile
@@ -226,8 +228,30 @@ module Sigh
226
228
  certificates
227
229
  end
228
230
 
231
+ def devices_to_use
232
+ # Only use devices if development or adhoc
233
+ return [] if !Sigh.config[:development] && !Sigh.config[:adhoc]
234
+
235
+ device_class = case Sigh.config[:platform].to_s
236
+ when 'ios'
237
+ [
238
+ Spaceship::ConnectAPI::Device::DeviceClass::APPLE_WATCH,
239
+ Spaceship::ConnectAPI::Device::DeviceClass::IPAD,
240
+ Spaceship::ConnectAPI::Device::DeviceClass::IPHONE,
241
+ Spaceship::ConnectAPI::Device::DeviceClass::IPOD
242
+ ].join(",")
243
+ when 'tvos'
244
+ Spaceship::ConnectAPI::Device::DeviceClass::APPLE_TV
245
+ when 'macos', 'catalyst'
246
+ Spaceship::ConnectAPI::Device::DeviceClass::MAC
247
+ end
248
+
249
+ filter = { deviceClass: device_class }
250
+ return Spaceship::ConnectAPI::Device.all(filter: filter)
251
+ end
252
+
229
253
  # Certificate to use based on the current distribution mode
230
- def certificate_to_use
254
+ def certificates_to_use
231
255
  certificates = certificates_for_profile_and_platform
232
256
 
233
257
  # Filter them
@@ -277,7 +301,7 @@ module Sigh
277
301
  end
278
302
 
279
303
  return certificates if Sigh.config[:development] # development profiles support multiple certificates
280
- return certificates.first
304
+ return [certificates.first]
281
305
  end
282
306
 
283
307
  # Downloads and stores the provisioning profile
@@ -24,7 +24,9 @@ module Snapshot
24
24
  options = []
25
25
  options += project_path_array
26
26
  options << "-sdk '#{config[:sdk]}'" if config[:sdk]
27
- options << "-derivedDataPath '#{derived_data_path}'"
27
+ if derived_data_path && !options.include?("-derivedDataPath #{derived_data_path.shellescape}")
28
+ options << "-derivedDataPath #{derived_data_path.shellescape}"
29
+ end
28
30
  options << "-resultBundlePath '#{result_bundle_path}'" if result_bundle_path
29
31
  if FastlaneCore::Helper.xcode_at_least?(11)
30
32
  options << "-testPlan '#{config[:testplan]}'" if config[:testplan]
@@ -69,14 +69,15 @@ module Spaceship
69
69
  return resps.flat_map(&:to_models)
70
70
  end
71
71
 
72
- def self.create(name: nil, profile_type: nil, bundle_id_id: nil, certificate_ids: nil, device_ids: nil)
72
+ def self.create(name: nil, profile_type: nil, bundle_id_id: nil, certificate_ids: nil, device_ids: nil, template_name: nil)
73
73
  resp = Spaceship::ConnectAPI.post_profiles(
74
74
  bundle_id_id: bundle_id_id,
75
75
  certificates: certificate_ids,
76
76
  devices: device_ids,
77
77
  attributes: {
78
78
  name: name,
79
- profileType: profile_type
79
+ profileType: profile_type,
80
+ templateName: template_name
80
81
  }
81
82
  )
82
83
  return resp.to_models.first
@@ -65,10 +65,10 @@ module Spaceship
65
65
  end
66
66
  },
67
67
  devices: {
68
- data: (devices || []).map do |certificate|
68
+ data: (devices || []).map do |device|
69
69
  {
70
70
  type: "devices",
71
- id: devices
71
+ id: device
72
72
  }
73
73
  end
74
74
  }
metadata CHANGED
@@ -1,35 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.155.0
4
+ version: 2.156.1
5
5
  platform: ruby
6
6
  authors:
7
+ - Aaron Brager
8
+ - Luka Mirosevic
9
+ - Maksym Grebenets
10
+ - Jorge Revuelta H
7
11
  - Manu Wallner
8
- - Helmut Januschka
9
- - Jérôme Lacoste
10
- - Fumiya Nakamura
11
- - Felix Krause
12
- - Jimmy Dee
12
+ - Stefan Natchev
13
+ - Andrew McBurney
14
+ - Matthew Ellis
15
+ - Josh Holtz
16
+ - Max Ott
13
17
  - Iulian Onofrei
14
- - Danielle Tomlinson
15
- - Maksym Grebenets
16
- - Jan Piotrowski
17
- - Luka Mirosevic
18
18
  - Joshua Liebowitz
19
+ - Jimmy Dee
19
20
  - Daniel Jankowski
20
- - Max Ott
21
- - Olivier Halligon
22
- - Josh Holtz
23
- - Matthew Ellis
24
- - Stefan Natchev
25
- - Jorge Revuelta H
21
+ - Jan Piotrowski
26
22
  - Kohki Miki
27
- - Aaron Brager
28
- - Andrew McBurney
23
+ - Jérôme Lacoste
24
+ - Fumiya Nakamura
25
+ - Olivier Halligon
26
+ - Felix Krause
27
+ - Danielle Tomlinson
28
+ - Helmut Januschka
29
29
  autorequire:
30
30
  bindir: bin
31
31
  cert_chain: []
32
- date: 2020-08-06 00:00:00.000000000 Z
32
+ date: 2020-08-13 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: slack-notifier
@@ -944,6 +944,7 @@ files:
944
944
  - deliver/lib/assets/summary.html.erb
945
945
  - deliver/lib/deliver.rb
946
946
  - deliver/lib/deliver/app_screenshot.rb
947
+ - deliver/lib/deliver/app_screenshot_iterator.rb
947
948
  - deliver/lib/deliver/commands_generator.rb
948
949
  - deliver/lib/deliver/detect_values.rb
949
950
  - deliver/lib/deliver/download_screenshots.rb
@@ -952,6 +953,7 @@ files:
952
953
  - deliver/lib/deliver/loader.rb
953
954
  - deliver/lib/deliver/module.rb
954
955
  - deliver/lib/deliver/options.rb
956
+ - deliver/lib/deliver/queue_worker.rb
955
957
  - deliver/lib/deliver/runner.rb
956
958
  - deliver/lib/deliver/setup.rb
957
959
  - deliver/lib/deliver/submit_for_review.rb
@@ -1608,6 +1610,7 @@ files:
1608
1610
  - spaceship/lib/spaceship/client.rb
1609
1611
  - spaceship/lib/spaceship/commands_generator.rb
1610
1612
  - spaceship/lib/spaceship/connect_api.rb
1613
+ - spaceship/lib/spaceship/connect_api/.client.rb.swp
1611
1614
  - spaceship/lib/spaceship/connect_api/client.rb
1612
1615
  - spaceship/lib/spaceship/connect_api/file_uploader.rb
1613
1616
  - spaceship/lib/spaceship/connect_api/model.rb