fastlane 2.155.0 → 2.156.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +74 -74
- data/deliver/lib/deliver/app_screenshot_iterator.rb +98 -0
- data/deliver/lib/deliver/queue_worker.rb +64 -0
- data/deliver/lib/deliver/upload_screenshots.rb +123 -125
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +1 -1
- data/fastlane/swift/Fastlane.swift +1 -1
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +1 -1
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +1 -1
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/ScanfileProtocol.swift +1 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
- data/fastlane_core/lib/fastlane_core/project.rb +1 -0
- data/gym/lib/gym/generators/build_command_generator.rb +0 -1
- data/scan/lib/scan/test_command_generator.rb +3 -1
- data/screengrab/lib/screengrab/runner.rb +7 -7
- data/sigh/lib/sigh/runner.rb +27 -3
- data/snapshot/lib/snapshot/test_command_generator_base.rb +3 -1
- data/spaceship/lib/spaceship/connect_api/.client.rb.swp +0 -0
- data/spaceship/lib/spaceship/connect_api/models/profile.rb +3 -2
- data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +2 -2
- metadata +22 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ddb9f8d09ffcb09f5ddd4d6365f67d41c538e8804630e40911e058a8174aaf2
|
4
|
+
data.tar.gz: 8bf0f6513c658bb9891058e5bee6fb22689bc496376e4aff6a2d2e5f1a2b9b74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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='
|
62
|
-
<a href='https://github.com/
|
63
|
-
<img src='https://github.com/
|
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/
|
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='
|
82
|
-
<a href='https://github.com/
|
83
|
-
<img src='https://github.com/
|
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/
|
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='
|
108
|
-
<a href='https://github.com/
|
109
|
-
<img src='https://github.com/
|
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/
|
85
|
+
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
118
86
|
</td>
|
119
|
-
<td id='
|
120
|
-
<a href='https://github.com/
|
121
|
-
<img src='https://github.com/
|
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'>
|
91
|
+
<h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
|
124
92
|
</td>
|
125
|
-
<td id='
|
126
|
-
<a href='https://github.com/
|
127
|
-
<img src='https://github.com/
|
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/
|
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='
|
134
|
-
<a href='https://github.com/
|
135
|
-
<img src='https://github.com/
|
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'
|
105
|
+
<h4 align='center'>Jimmy Dee</h4>
|
138
106
|
</td>
|
139
|
-
<td id='
|
140
|
-
<a href='https://github.com/
|
141
|
-
<img src='https://github.com/
|
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/
|
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='
|
166
|
-
<a href='https://github.com/
|
167
|
-
<img src='https://github.com/
|
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/
|
169
|
+
<h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
|
170
170
|
</td>
|
171
|
-
<td id='
|
172
|
-
<a href='https://github.com/
|
173
|
-
<img src='https://github.com/
|
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/
|
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(
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
96
|
-
|
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 =
|
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
|
118
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
159
|
-
|
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
|
-
|
137
|
+
checksum = UploadScreenshots.calculate_checksum(screenshot.path)
|
138
|
+
duplicate = (app_screenshot_set.app_screenshots || []).any? { |s| s.source_file_checksum == checksum }
|
164
139
|
|
165
|
-
#
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
172
|
-
|
173
|
-
count: app_screenshot_set.app_screenshots.size,
|
174
|
-
checksums: []
|
175
|
-
}
|
147
|
+
number_of_screenshots += 1
|
148
|
+
end
|
176
149
|
|
177
|
-
|
178
|
-
indized[localization.locale][app_screenshot_set.screenshot_display_type][:checksums] = checksums
|
179
|
-
end
|
150
|
+
worker.start
|
180
151
|
|
181
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
192
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
171
|
+
is_processing = states.fetch('UPLOAD_COMPLETE', 0) > 0
|
172
|
+
return states unless is_processing
|
204
173
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
174
|
+
UI.verbose("There are still incomplete screenshots - #{states}")
|
175
|
+
sleep(5)
|
176
|
+
end
|
177
|
+
end
|
209
178
|
|
210
|
-
|
211
|
-
|
212
|
-
|
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
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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
|
|
data/sigh/lib/sigh/runner.rb
CHANGED
@@ -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:
|
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
|
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
|
-
|
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]
|
Binary file
|
@@ -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
|
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.
|
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
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
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
|
-
-
|
21
|
-
- Olivier Halligon
|
22
|
-
- Josh Holtz
|
23
|
-
- Matthew Ellis
|
24
|
-
- Stefan Natchev
|
25
|
-
- Jorge Revuelta H
|
21
|
+
- Jan Piotrowski
|
26
22
|
- Kohki Miki
|
27
|
-
-
|
28
|
-
-
|
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-
|
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
|