fastlane 2.190.0 → 2.191.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +88 -88
- data/deliver/lib/deliver/app_screenshot.rb +2 -1
- data/deliver/lib/deliver/app_screenshot_iterator.rb +2 -2
- data/deliver/lib/deliver/loader.rb +1 -1
- data/deliver/lib/deliver/options.rb +6 -0
- data/deliver/lib/deliver/runner.rb +9 -1
- data/deliver/lib/deliver/screenshot_comparable.rb +62 -0
- data/deliver/lib/deliver/sync_screenshots.rb +200 -0
- data/fastlane/lib/fastlane/actions/zip.rb +3 -2
- data/fastlane/lib/fastlane/features.rb +3 -0
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +5 -1
- data/fastlane/swift/Fastlane.swift +13 -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/swift/formatting/Brewfile.lock.json +2 -2
- data/produce/lib/produce/commands_generator.rb +28 -0
- data/produce/lib/produce/service.rb +15 -0
- data/spaceship/lib/spaceship/connect_api.rb +1 -0
- data/spaceship/lib/spaceship/connect_api/models/app.rb +7 -0
- data/spaceship/lib/spaceship/connect_api/models/capabilities.rb +27 -0
- data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +5 -0
- data/spaceship/lib/spaceship/connect_api/users/users.rb +34 -1
- data/supply/lib/supply/uploader.rb +1 -1
- metadata +24 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 191c5f2f91fe567ba6f0c35848e69c0b921480ef1f2076bd531b774b6773bada
|
4
|
+
data.tar.gz: 6cf068574ea7ea72ac092791021fc78c2d1486d9e6f03772cc9564420e98a989
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aacb03423627e0dc324915167515f0a4c8faebd6349c9a989320332baa668782f7dfbd0c20899e0905fcffc3d3ad3f8e254e05f75b30facff9744636b37c3c00
|
7
|
+
data.tar.gz: fa30c4044f3e3dffa7b53ed4a48d9e6fa2805649538077e2134f41eeada06e9bc66f4821124a5e59fb29542727aa8df9b278660ae522fbab134bee4427c6bc13
|
data/README.md
CHANGED
@@ -35,11 +35,11 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
35
35
|
<!-- This table is regenerated and resorted on each release -->
|
36
36
|
<table id='team'>
|
37
37
|
<tr>
|
38
|
-
<td id='
|
39
|
-
<a href='https://github.com/
|
40
|
-
<img src='https://github.com/
|
38
|
+
<td id='josh-holtz'>
|
39
|
+
<a href='https://github.com/joshdholtz'>
|
40
|
+
<img src='https://github.com/joshdholtz.png' width='140px;'>
|
41
41
|
</a>
|
42
|
-
<h4 align='center'><a href='https://twitter.com/
|
42
|
+
<h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
|
43
43
|
</td>
|
44
44
|
<td id='aaron-brager'>
|
45
45
|
<a href='https://github.com/getaaron'>
|
@@ -47,11 +47,17 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
47
47
|
</a>
|
48
48
|
<h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
|
49
49
|
</td>
|
50
|
-
<td id='
|
51
|
-
<a href='https://github.com/
|
52
|
-
<img src='https://github.com/
|
50
|
+
<td id='daniel-jankowski'>
|
51
|
+
<a href='https://github.com/mollyIV'>
|
52
|
+
<img src='https://github.com/mollyIV.png' width='140px;'>
|
53
53
|
</a>
|
54
|
-
<h4 align='center'><a href='https://twitter.com/
|
54
|
+
<h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
|
55
|
+
</td>
|
56
|
+
<td id='luka-mirosevic'>
|
57
|
+
<a href='https://github.com/lmirosevic'>
|
58
|
+
<img src='https://github.com/lmirosevic.png' width='140px;'>
|
59
|
+
</a>
|
60
|
+
<h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
|
55
61
|
</td>
|
56
62
|
<td id='satoshi-namai'>
|
57
63
|
<a href='https://github.com/ainame'>
|
@@ -59,51 +65,51 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
59
65
|
</a>
|
60
66
|
<h4 align='center'><a href='https://twitter.com/ainame'>Satoshi Namai</a></h4>
|
61
67
|
</td>
|
62
|
-
<td id='manish-rathi'>
|
63
|
-
<a href='https://github.com/crazymanish'>
|
64
|
-
<img src='https://github.com/crazymanish.png' width='140px;'>
|
65
|
-
</a>
|
66
|
-
<h4 align='center'><a href='https://twitter.com/iammanishrathi'>Manish Rathi</a></h4>
|
67
|
-
</td>
|
68
68
|
</tr>
|
69
69
|
<tr>
|
70
|
-
<td id='
|
71
|
-
<a href='https://github.com/
|
72
|
-
<img src='https://github.com/
|
70
|
+
<td id='max-ott'>
|
71
|
+
<a href='https://github.com/max-ott'>
|
72
|
+
<img src='https://github.com/max-ott.png' width='140px;'>
|
73
73
|
</a>
|
74
|
-
<h4 align='center'><a href='https://twitter.com/
|
74
|
+
<h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
|
75
75
|
</td>
|
76
|
-
<td id='
|
77
|
-
<a href='https://github.com/
|
78
|
-
<img src='https://github.com/
|
76
|
+
<td id='maksym-grebenets'>
|
77
|
+
<a href='https://github.com/mgrebenets'>
|
78
|
+
<img src='https://github.com/mgrebenets.png' width='140px;'>
|
79
79
|
</a>
|
80
|
-
<h4 align='center'><a href='https://twitter.com/
|
80
|
+
<h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
|
81
81
|
</td>
|
82
|
-
<td id='
|
83
|
-
<a href='https://github.com/
|
84
|
-
<img src='https://github.com/
|
82
|
+
<td id='felix-krause'>
|
83
|
+
<a href='https://github.com/KrauseFx'>
|
84
|
+
<img src='https://github.com/KrauseFx.png' width='140px;'>
|
85
85
|
</a>
|
86
|
-
<h4 align='center'><a href='https://twitter.com/
|
86
|
+
<h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
|
87
87
|
</td>
|
88
|
-
<td id='
|
89
|
-
<a href='https://github.com/
|
90
|
-
<img src='https://github.com/
|
88
|
+
<td id='kohki-miki'>
|
89
|
+
<a href='https://github.com/giginet'>
|
90
|
+
<img src='https://github.com/giginet.png' width='140px;'>
|
91
91
|
</a>
|
92
|
-
<h4 align='center'><a href='https://twitter.com/
|
92
|
+
<h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
|
93
93
|
</td>
|
94
|
-
<td id='
|
95
|
-
<a href='https://github.com/
|
96
|
-
<img src='https://github.com/
|
94
|
+
<td id='helmut-januschka'>
|
95
|
+
<a href='https://github.com/hjanuschka'>
|
96
|
+
<img src='https://github.com/hjanuschka.png' width='140px;'>
|
97
97
|
</a>
|
98
|
-
<h4 align='center'><a href='https://twitter.com/
|
98
|
+
<h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
|
99
99
|
</td>
|
100
100
|
</tr>
|
101
101
|
<tr>
|
102
|
-
<td id='
|
103
|
-
<a href='https://github.com/
|
104
|
-
<img src='https://github.com/
|
102
|
+
<td id='jimmy-dee'>
|
103
|
+
<a href='https://github.com/jdee'>
|
104
|
+
<img src='https://github.com/jdee.png' width='140px;'>
|
105
105
|
</a>
|
106
|
-
<h4 align='center'
|
106
|
+
<h4 align='center'>Jimmy Dee</h4>
|
107
|
+
</td>
|
108
|
+
<td id='roger-oba'>
|
109
|
+
<a href='https://github.com/rogerluan'>
|
110
|
+
<img src='https://github.com/rogerluan.png' width='140px;'>
|
111
|
+
</a>
|
112
|
+
<h4 align='center'><a href='https://twitter.com/rogerluan_'>Roger Oba</a></h4>
|
107
113
|
</td>
|
108
114
|
<td id='matthew-ellis'>
|
109
115
|
<a href='https://github.com/matthewellis'>
|
@@ -111,31 +117,31 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
111
117
|
</a>
|
112
118
|
<h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
|
113
119
|
</td>
|
114
|
-
<td id='
|
115
|
-
<a href='https://github.com/
|
116
|
-
<img src='https://github.com/
|
117
|
-
</a>
|
118
|
-
<h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
|
119
|
-
</td>
|
120
|
-
<td id='maksym-grebenets'>
|
121
|
-
<a href='https://github.com/mgrebenets'>
|
122
|
-
<img src='https://github.com/mgrebenets.png' width='140px;'>
|
120
|
+
<td id='jorge-revuelta-h'>
|
121
|
+
<a href='https://github.com/minuscorp'>
|
122
|
+
<img src='https://github.com/minuscorp.png' width='140px;'>
|
123
123
|
</a>
|
124
|
-
<h4 align='center'><a href='https://twitter.com/
|
124
|
+
<h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
|
125
125
|
</td>
|
126
|
-
<td id='
|
127
|
-
<a href='https://github.com/
|
128
|
-
<img src='https://github.com/
|
126
|
+
<td id='jan-piotrowski'>
|
127
|
+
<a href='https://github.com/janpio'>
|
128
|
+
<img src='https://github.com/janpio.png' width='140px;'>
|
129
129
|
</a>
|
130
|
-
<h4 align='center'><a href='https://twitter.com/
|
130
|
+
<h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
|
131
131
|
</td>
|
132
132
|
</tr>
|
133
133
|
<tr>
|
134
|
-
<td id='
|
135
|
-
<a href='https://github.com/
|
136
|
-
<img src='https://github.com/
|
134
|
+
<td id='manish-rathi'>
|
135
|
+
<a href='https://github.com/crazymanish'>
|
136
|
+
<img src='https://github.com/crazymanish.png' width='140px;'>
|
137
137
|
</a>
|
138
|
-
<h4 align='center'>
|
138
|
+
<h4 align='center'><a href='https://twitter.com/iammanishrathi'>Manish Rathi</a></h4>
|
139
|
+
</td>
|
140
|
+
<td id='danielle-tomlinson'>
|
141
|
+
<a href='https://github.com/endocrimes'>
|
142
|
+
<img src='https://github.com/endocrimes.png' width='140px;'>
|
143
|
+
</a>
|
144
|
+
<h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
|
139
145
|
</td>
|
140
146
|
<td id='joshua-liebowitz'>
|
141
147
|
<a href='https://github.com/taquitos'>
|
@@ -143,55 +149,49 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
143
149
|
</a>
|
144
150
|
<h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
|
145
151
|
</td>
|
152
|
+
<td id='fumiya-nakamura'>
|
153
|
+
<a href='https://github.com/nafu'>
|
154
|
+
<img src='https://github.com/nafu.png' width='140px;'>
|
155
|
+
</a>
|
156
|
+
<h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
|
157
|
+
</td>
|
146
158
|
<td id='jérôme-lacoste'>
|
147
159
|
<a href='https://github.com/lacostej'>
|
148
160
|
<img src='https://github.com/lacostej.png' width='140px;'>
|
149
161
|
</a>
|
150
162
|
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
151
163
|
</td>
|
152
|
-
<td id='stefan-natchev'>
|
153
|
-
<a href='https://github.com/snatchev'>
|
154
|
-
<img src='https://github.com/snatchev.png' width='140px;'>
|
155
|
-
</a>
|
156
|
-
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
157
|
-
</td>
|
158
|
-
<td id='iulian-onofrei'>
|
159
|
-
<a href='https://github.com/revolter'>
|
160
|
-
<img src='https://github.com/revolter.png' width='140px;'>
|
161
|
-
</a>
|
162
|
-
<h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
|
163
|
-
</td>
|
164
164
|
</tr>
|
165
165
|
<tr>
|
166
|
-
<td id='
|
167
|
-
<a href='https://github.com/
|
168
|
-
<img src='https://github.com/
|
166
|
+
<td id='manu-wallner'>
|
167
|
+
<a href='https://github.com/milch'>
|
168
|
+
<img src='https://github.com/milch.png' width='140px;'>
|
169
169
|
</a>
|
170
|
-
<h4 align='center'><a href='https://twitter.com/
|
170
|
+
<h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
|
171
171
|
</td>
|
172
|
-
<td id='
|
173
|
-
<a href='https://github.com/
|
174
|
-
<img src='https://github.com/
|
172
|
+
<td id='andrew-mcburney'>
|
173
|
+
<a href='https://github.com/armcburney'>
|
174
|
+
<img src='https://github.com/armcburney.png' width='140px;'>
|
175
175
|
</a>
|
176
|
-
<h4 align='center'><a href='https://twitter.com/
|
176
|
+
<h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
|
177
177
|
</td>
|
178
|
-
<td id='
|
179
|
-
<a href='https://github.com/
|
180
|
-
<img src='https://github.com/
|
178
|
+
<td id='olivier-halligon'>
|
179
|
+
<a href='https://github.com/AliSoftware'>
|
180
|
+
<img src='https://github.com/AliSoftware.png' width='140px;'>
|
181
181
|
</a>
|
182
|
-
<h4 align='center'><a href='https://twitter.com/
|
182
|
+
<h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
|
183
183
|
</td>
|
184
|
-
<td id='
|
185
|
-
<a href='https://github.com/
|
186
|
-
<img src='https://github.com/
|
184
|
+
<td id='iulian-onofrei'>
|
185
|
+
<a href='https://github.com/revolter'>
|
186
|
+
<img src='https://github.com/revolter.png' width='140px;'>
|
187
187
|
</a>
|
188
|
-
<h4 align='center'><a href='https://twitter.com/
|
188
|
+
<h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
|
189
189
|
</td>
|
190
|
-
<td id='
|
191
|
-
<a href='https://github.com/
|
192
|
-
<img src='https://github.com/
|
190
|
+
<td id='stefan-natchev'>
|
191
|
+
<a href='https://github.com/snatchev'>
|
192
|
+
<img src='https://github.com/snatchev.png' width='140px;'>
|
193
193
|
</a>
|
194
|
-
<h4 align='center'><a href='https://twitter.com/
|
194
|
+
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
195
195
|
</td>
|
196
196
|
</tr>
|
197
197
|
</table>
|
@@ -324,7 +324,8 @@ module Deliver
|
|
324
324
|
is_3rd_gen = [
|
325
325
|
"iPad Pro (12.9-inch) (3rd generation)", # default simulator name has this
|
326
326
|
"iPad Pro (12.9-inch) (4th generation)", # default simulator name has this
|
327
|
-
"ipadPro129" # downloaded screenshots name has this
|
327
|
+
"ipadPro129", # downloaded screenshots name has this,
|
328
|
+
"3GEN" # downloaded screenshots name from App Store Connect API has this
|
328
329
|
].any? { |key| filename.include?(key) }
|
329
330
|
if is_3rd_gen
|
330
331
|
if screen_size == ScreenSize::IOS_IPAD_PRO
|
@@ -85,8 +85,8 @@ module Deliver
|
|
85
85
|
app_screenshot_set ||= localization.create_app_screenshot_set(attributes: { screenshotDisplayType: display_type })
|
86
86
|
|
87
87
|
# iterate over screenshots per display size with index
|
88
|
-
screenshots.each do |screenshot|
|
89
|
-
yield(localization, app_screenshot_set, screenshot)
|
88
|
+
screenshots.each.with_index do |screenshot, index|
|
89
|
+
yield(localization, app_screenshot_set, screenshot, index)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end
|
@@ -82,7 +82,7 @@ module Deliver
|
|
82
82
|
#
|
83
83
|
# @param root [String] A directory path
|
84
84
|
# @param ignore_validation [String] Set false not to raise the error when finding invalid folder name
|
85
|
-
# @return [Array<AppScreenshot>] The list of AppScreenshot that exist under given `root` directory
|
85
|
+
# @return [Array<Deliver::AppScreenshot>] The list of AppScreenshot that exist under given `root` directory
|
86
86
|
def self.load_app_screenshots(root, ignore_validation)
|
87
87
|
screenshots = language_folders(root, ignore_validation, true).flat_map do |language_folder|
|
88
88
|
paths = if language_folder.framed_file_paths.count > 0
|
@@ -162,6 +162,12 @@ module Deliver
|
|
162
162
|
description: "Clear all previously uploaded screenshots before uploading the new ones",
|
163
163
|
type: Boolean,
|
164
164
|
default_value: false),
|
165
|
+
FastlaneCore::ConfigItem.new(key: :sync_screenshots,
|
166
|
+
env_name: "DELIVER_SYNC_SCREENSHOTS",
|
167
|
+
description: "Sync screenshots with local ones. This is currently beta option" \
|
168
|
+
"so set true to 'FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS' environment variable as well",
|
169
|
+
type: Boolean,
|
170
|
+
default_value: false),
|
165
171
|
FastlaneCore::ConfigItem.new(key: :submit_for_review,
|
166
172
|
env_name: "DELIVER_SUBMIT_FOR_REVIEW",
|
167
173
|
description: "Submit the new version for Review after uploading everything",
|
@@ -10,6 +10,7 @@ require_relative 'submit_for_review'
|
|
10
10
|
require_relative 'upload_price_tier'
|
11
11
|
require_relative 'upload_metadata'
|
12
12
|
require_relative 'upload_screenshots'
|
13
|
+
require_relative 'sync_screenshots'
|
13
14
|
require_relative 'detect_values'
|
14
15
|
|
15
16
|
module Deliver
|
@@ -143,7 +144,14 @@ module Deliver
|
|
143
144
|
|
144
145
|
# Commit
|
145
146
|
upload_metadata.upload(options)
|
146
|
-
|
147
|
+
|
148
|
+
if options[:sync_screenshots]
|
149
|
+
sync_screenshots = SyncScreenshots.new(app: Deliver.cache[:app], platform: Spaceship::ConnectAPI::Platform.map(options[:platform]))
|
150
|
+
sync_screenshots.sync(screenshots)
|
151
|
+
else
|
152
|
+
upload_screenshots.upload(options, screenshots)
|
153
|
+
end
|
154
|
+
|
147
155
|
UploadPriceTier.new.upload(options)
|
148
156
|
end
|
149
157
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spaceship/connect_api/models/app_screenshot'
|
2
|
+
require 'spaceship/connect_api/models/app_screenshot_set'
|
3
|
+
|
4
|
+
require_relative 'app_screenshot'
|
5
|
+
|
6
|
+
module Deliver
|
7
|
+
# This clsas enables you to compare equality between different representations of the screenshots
|
8
|
+
# in the standard API `Array#-` that requires objects to implements `eql?` and `hash`.
|
9
|
+
class ScreenshotComparable
|
10
|
+
# A unique key value that is consist of locale, filename, and checksum.
|
11
|
+
attr_reader :key
|
12
|
+
|
13
|
+
# A hash object that contains the source data of this representation class
|
14
|
+
attr_reader :context
|
15
|
+
|
16
|
+
def self.create_from_local(screenshot:, app_screenshot_set:)
|
17
|
+
raise ArgumentError unless screenshot.kind_of?(Deliver::AppScreenshot)
|
18
|
+
raise ArgumentError unless app_screenshot_set.kind_of?(Spaceship::ConnectAPI::AppScreenshotSet)
|
19
|
+
|
20
|
+
new(
|
21
|
+
path: "#{screenshot.language}/#{File.basename(screenshot.path)}",
|
22
|
+
checksum: calculate_checksum(screenshot.path),
|
23
|
+
context: {
|
24
|
+
screenshot: screenshot,
|
25
|
+
app_screenshot_set: app_screenshot_set
|
26
|
+
}
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.create_from_remote(app_screenshot:, locale:)
|
31
|
+
raise ArgumentError unless app_screenshot.kind_of?(Spaceship::ConnectAPI::AppScreenshot)
|
32
|
+
raise ArgumentError unless locale.kind_of?(String)
|
33
|
+
|
34
|
+
new(
|
35
|
+
path: "#{locale}/#{app_screenshot.file_name}",
|
36
|
+
checksum: app_screenshot.source_file_checksum,
|
37
|
+
context: {
|
38
|
+
app_screenshot: app_screenshot,
|
39
|
+
locale: locale
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.calculate_checksum(path)
|
45
|
+
bytes = File.binread(path)
|
46
|
+
Digest::MD5.hexdigest(bytes)
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(path:, checksum:, context:)
|
50
|
+
@key = "#{path}/#{checksum}"
|
51
|
+
@context = context
|
52
|
+
end
|
53
|
+
|
54
|
+
def eql?(other)
|
55
|
+
key == other.key
|
56
|
+
end
|
57
|
+
|
58
|
+
def hash
|
59
|
+
key.hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'fastlane_core'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'naturally'
|
4
|
+
|
5
|
+
require_relative 'app_screenshot'
|
6
|
+
require_relative 'app_screenshot_iterator'
|
7
|
+
require_relative 'loader'
|
8
|
+
require_relative 'screenshot_comparable'
|
9
|
+
|
10
|
+
module Deliver
|
11
|
+
class SyncScreenshots
|
12
|
+
DeleteScreenshotJob = Struct.new(:app_screenshot, :locale)
|
13
|
+
UploadScreenshotJob = Struct.new(:app_screenshot_set, :path)
|
14
|
+
|
15
|
+
class UploadResult
|
16
|
+
attr_reader :asset_delivery_state_counts, :failing_screenshots
|
17
|
+
|
18
|
+
def initialize(asset_delivery_state_counts:, failing_screenshots:)
|
19
|
+
@asset_delivery_state_counts = asset_delivery_state_counts
|
20
|
+
@failing_screenshots = failing_screenshots
|
21
|
+
end
|
22
|
+
|
23
|
+
def processing?
|
24
|
+
@asset_delivery_state_counts.fetch('UPLOAD_COMPLETE', 0) > 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def screenshot_count
|
28
|
+
@asset_delivery_state_counts.fetch('COMPLETE', 0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(app:, platform:)
|
33
|
+
@app = app
|
34
|
+
@platform = platform
|
35
|
+
end
|
36
|
+
|
37
|
+
def sync_from_path(screenshots_path)
|
38
|
+
# load local screenshots
|
39
|
+
screenshots = Deliver::Loader.load_app_screenshots(screenshots_path, true)
|
40
|
+
sync(screenshots)
|
41
|
+
end
|
42
|
+
|
43
|
+
def sync(screenshots)
|
44
|
+
UI.important('This is currently a beta feature in fastlane. This may cause some errors on your environment.')
|
45
|
+
|
46
|
+
unless FastlaneCore::Feature.enabled?('FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS')
|
47
|
+
UI.user_error!('Please set a value to "FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS" environment variable ' \
|
48
|
+
'if you acknowleage the risk and try this out.')
|
49
|
+
end
|
50
|
+
|
51
|
+
UI.important("Will begin uploading snapshots for '#{version.version_string}' on App Store Connect")
|
52
|
+
|
53
|
+
# enable localizations that will be used
|
54
|
+
screenshots_per_language = screenshots.group_by(&:language)
|
55
|
+
enable_localizations(screenshots_per_language.keys)
|
56
|
+
|
57
|
+
# create iterator
|
58
|
+
localizations = fetch_localizations
|
59
|
+
iterator = Deliver::AppScreenshotIterator.new(localizations)
|
60
|
+
|
61
|
+
# sync local screenshots with remote settings by deleting and uploading
|
62
|
+
UI.message("Starting with the upload of screenshots...")
|
63
|
+
replace_screenshots(iterator, screenshots)
|
64
|
+
|
65
|
+
# ensure screenshots within screenshot sets are sorted in right order
|
66
|
+
sort_screenshots(iterator)
|
67
|
+
|
68
|
+
UI.important('Screenshots are synced successfully!')
|
69
|
+
end
|
70
|
+
|
71
|
+
def enable_localizations(locales)
|
72
|
+
localizations = fetch_localizations
|
73
|
+
locales_to_enable = locales - localizations.map(&:locale)
|
74
|
+
Helper.show_loading_indicator("Activating localizations for #{locales_to_enable.join(', ')}...")
|
75
|
+
locales_to_enable.each do |locale|
|
76
|
+
version.create_app_store_version_localization(attributes: { locale: locale })
|
77
|
+
end
|
78
|
+
Helper.hide_loading_indicator
|
79
|
+
end
|
80
|
+
|
81
|
+
def replace_screenshots(iterator, screenshots, retries = 3)
|
82
|
+
# delete and upload screenshots to get App Store Connect in sync
|
83
|
+
do_replace_screenshots(iterator, screenshots, create_delete_worker, create_upload_worker)
|
84
|
+
|
85
|
+
# wait for screenshots to be processed on App Store Connect end and
|
86
|
+
# ensure the number of uploaded screenshots matches the one in local
|
87
|
+
result = wait_for_complete(iterator)
|
88
|
+
return if !result.processing? && result.screenshot_count == screenshots.count
|
89
|
+
|
90
|
+
if retries.zero?
|
91
|
+
UI.crash!("Retried uploading screenshots #{retries} but there are still failures of processing screenshots." \
|
92
|
+
"Check App Store Connect console to work out which screenshots processed unsuccessfully.")
|
93
|
+
end
|
94
|
+
|
95
|
+
# retry with deleting failing screenshots
|
96
|
+
result.failing_screenshots.each(&:delete!)
|
97
|
+
replace_screenshots(iterator, screenshots, retries - 1)
|
98
|
+
end
|
99
|
+
|
100
|
+
# This is a testable method that focuses on figuring out what to update
|
101
|
+
def do_replace_screenshots(iterator, screenshots, delete_worker, upload_worker)
|
102
|
+
remote_screenshots = iterator.each_app_screenshot.map do |localization, app_screenshot_set, app_screenshot|
|
103
|
+
ScreenshotComparable.create_from_remote(app_screenshot: app_screenshot, locale: localization.locale)
|
104
|
+
end
|
105
|
+
|
106
|
+
local_screenshots = iterator.each_local_screenshot(screenshots.group_by(&:language)).map do |localization, app_screenshot_set, screenshot, index|
|
107
|
+
if index >= 10
|
108
|
+
UI.user_error!("Found #{localization.locale} has more than 10 screenshots for #{app_screenshot_set.screenshot_display_type}. "\
|
109
|
+
"Make sure containts only necessary screenshots.")
|
110
|
+
end
|
111
|
+
ScreenshotComparable.create_from_local(screenshot: screenshot, app_screenshot_set: app_screenshot_set)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Thanks to `Array#-` API and `ScreenshotComparable`, working out diffs between local screenshot directory and App Store Connect
|
115
|
+
# is as easy as you can see below. The former one finds what is missing in local and the latter one is visa versa.
|
116
|
+
screenshots_to_delete = remote_screenshots - local_screenshots
|
117
|
+
screenshots_to_upload = local_screenshots - remote_screenshots
|
118
|
+
|
119
|
+
delete_jobs = screenshots_to_delete.map { |x| DeleteScreenshotJob.new(x.context[:app_screenshot], x.context[:locale]) }
|
120
|
+
delete_worker.batch_enqueue(delete_jobs)
|
121
|
+
delete_worker.start
|
122
|
+
|
123
|
+
upload_jobs = screenshots_to_upload.map { |x| UploadScreenshotJob.new(x.context[:app_screenshot_set], x.context[:screenshot].path) }
|
124
|
+
upload_worker.batch_enqueue(upload_jobs)
|
125
|
+
upload_worker.start
|
126
|
+
end
|
127
|
+
|
128
|
+
def wait_for_complete(iterator)
|
129
|
+
retry_count = 0
|
130
|
+
Helper.show_loading_indicator("Waiting for all the screenshots processed...")
|
131
|
+
loop do
|
132
|
+
failing_screenshots = []
|
133
|
+
state_counts = iterator.each_app_screenshot.map { |_, _, app_screenshot| app_screenshot }.each_with_object({}) do |app_screenshot, hash|
|
134
|
+
state = app_screenshot.asset_delivery_state['state']
|
135
|
+
hash[state] ||= 0
|
136
|
+
hash[state] += 1
|
137
|
+
failing_screenshots << app_screenshot if app_screenshot.error?
|
138
|
+
end
|
139
|
+
|
140
|
+
result = UploadResult.new(asset_delivery_state_counts: state_counts, failing_screenshots: failing_screenshots)
|
141
|
+
return result unless result.processing?
|
142
|
+
|
143
|
+
# sleep with exponential backoff
|
144
|
+
interval = 5 + (2**retry_count)
|
145
|
+
UI.message("There are still incomplete screenshots. Will check the states again in #{interval} secs - #{state_counts}")
|
146
|
+
sleep(interval)
|
147
|
+
retry_count += 1
|
148
|
+
end
|
149
|
+
ensure
|
150
|
+
Helper.hide_loading_indicator
|
151
|
+
end
|
152
|
+
|
153
|
+
def sort_screenshots(iterator)
|
154
|
+
Helper.show_loading_indicator("Sorting screenshots uploaded...")
|
155
|
+
sort_worker = create_sort_worker
|
156
|
+
sort_worker.batch_enqueue(iterator.each_app_screenshot_set.to_a.map { |_, set| set })
|
157
|
+
sort_worker.start
|
158
|
+
Helper.hide_loading_indicator
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def version
|
164
|
+
@version ||= @app.get_edit_app_store_version(platform: @platform)
|
165
|
+
end
|
166
|
+
|
167
|
+
def fetch_localizations
|
168
|
+
version.get_app_store_version_localizations
|
169
|
+
end
|
170
|
+
|
171
|
+
def create_upload_worker
|
172
|
+
FastlaneCore::QueueWorker.new do |job|
|
173
|
+
UI.verbose("Uploading '#{job.path}'...")
|
174
|
+
start_time = Time.now
|
175
|
+
job.app_screenshot_set.upload_screenshot(path: job.path, wait_for_processing: false)
|
176
|
+
UI.message("Uploaded '#{job.path}'... (#{Time.now - start_time} secs)")
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def create_delete_worker
|
181
|
+
FastlaneCore::QueueWorker.new do |job|
|
182
|
+
target = "id=#{job.app_screenshot.id} #{job.locale} #{job.app_screenshot.file_name}"
|
183
|
+
UI.verbose("Deleting '#{target}'")
|
184
|
+
start_time = Time.now
|
185
|
+
job.app_screenshot.delete!
|
186
|
+
UI.message("Deleted '#{target}' - (#{Time.now - start_time} secs)")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def create_sort_worker
|
191
|
+
FastlaneCore::QueueWorker.new do |app_screenshot_set|
|
192
|
+
original_ids = app_screenshot_set.app_screenshots.map(&:id)
|
193
|
+
sorted_ids = Naturally.sort(app_screenshot_set.app_screenshots, by: :file_name).map(&:id)
|
194
|
+
if original_ids != sorted_ids
|
195
|
+
app_screenshot_set.reorder_screenshots(app_screenshot_ids: sorted_ids)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -52,8 +52,8 @@ module Fastlane
|
|
52
52
|
# The zip command is executed from the paths **parent** directory, as a result we use just the basename, which is the file or folder within
|
53
53
|
basename = File.basename(path)
|
54
54
|
|
55
|
-
command << output_path
|
56
|
-
command << basename
|
55
|
+
command << output_path.shellescape
|
56
|
+
command << basename.shellescape
|
57
57
|
|
58
58
|
unless include.empty?
|
59
59
|
command << "-i"
|
@@ -68,6 +68,7 @@ module Fastlane
|
|
68
68
|
command
|
69
69
|
end
|
70
70
|
end
|
71
|
+
|
71
72
|
def self.run(params)
|
72
73
|
Runner.new(params).run
|
73
74
|
end
|
@@ -2,3 +2,6 @@
|
|
2
2
|
|
3
3
|
# FastlaneCore::Feature.register(env_var: 'YOUR_FEATURE_SWITCH_ENV_VAR',
|
4
4
|
# description: 'Describe what this feature switch controls')
|
5
|
+
|
6
|
+
FastlaneCore::Feature.register(env_var: 'FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS',
|
7
|
+
description: 'Use a newly implemented screenshots synchronization logic')
|
@@ -59,6 +59,9 @@ public protocol DeliverfileProtocol: class {
|
|
59
59
|
/// Clear all previously uploaded screenshots before uploading the new ones
|
60
60
|
var overwriteScreenshots: Bool { get }
|
61
61
|
|
62
|
+
/// Sync screenshots with local ones. This is currently beta optionso set true to 'FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS' environment variable as well
|
63
|
+
var syncScreenshots: Bool { get }
|
64
|
+
|
62
65
|
/// Submit the new version for Review after uploading everything
|
63
66
|
var submitForReview: Bool { get }
|
64
67
|
|
@@ -209,6 +212,7 @@ public extension DeliverfileProtocol {
|
|
209
212
|
var skipAppVersionUpdate: Bool { return false }
|
210
213
|
var force: Bool { return false }
|
211
214
|
var overwriteScreenshots: Bool { return false }
|
215
|
+
var syncScreenshots: Bool { return false }
|
212
216
|
var submitForReview: Bool { return false }
|
213
217
|
var rejectIfPossible: Bool { return false }
|
214
218
|
var automaticRelease: Bool? { return nil }
|
@@ -256,4 +260,4 @@ public extension DeliverfileProtocol {
|
|
256
260
|
|
257
261
|
// Please don't remove the lines below
|
258
262
|
// They are used to detect outdated files
|
259
|
-
// FastlaneRunnerAPIVersion [0.9.
|
263
|
+
// FastlaneRunnerAPIVersion [0.9.79]
|
@@ -661,6 +661,7 @@ public func appledoc(input: [String],
|
|
661
661
|
- skipAppVersionUpdate: Don’t create or update the app version that is being prepared for submission
|
662
662
|
- force: Skip verification of HTML preview file
|
663
663
|
- overwriteScreenshots: Clear all previously uploaded screenshots before uploading the new ones
|
664
|
+
- syncScreenshots: Sync screenshots with local ones. This is currently beta optionso set true to 'FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS' environment variable as well
|
664
665
|
- submitForReview: Submit the new version for Review after uploading everything
|
665
666
|
- rejectIfPossible: Rejects the previously submitted build if it's in a state where it's possible
|
666
667
|
- automaticRelease: Should the app be automatically released once it's approved? (Can not be used together with `auto_release_date`)
|
@@ -731,6 +732,7 @@ public func appstore(apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault
|
|
731
732
|
skipAppVersionUpdate: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
732
733
|
force: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
733
734
|
overwriteScreenshots: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
735
|
+
syncScreenshots: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
734
736
|
submitForReview: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
735
737
|
rejectIfPossible: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
736
738
|
automaticRelease: OptionalConfigValue<Bool?> = .fastlaneDefault(nil),
|
@@ -794,6 +796,7 @@ public func appstore(apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault
|
|
794
796
|
let skipAppVersionUpdateArg = skipAppVersionUpdate.asRubyArgument(name: "skip_app_version_update", type: nil)
|
795
797
|
let forceArg = force.asRubyArgument(name: "force", type: nil)
|
796
798
|
let overwriteScreenshotsArg = overwriteScreenshots.asRubyArgument(name: "overwrite_screenshots", type: nil)
|
799
|
+
let syncScreenshotsArg = syncScreenshots.asRubyArgument(name: "sync_screenshots", type: nil)
|
797
800
|
let submitForReviewArg = submitForReview.asRubyArgument(name: "submit_for_review", type: nil)
|
798
801
|
let rejectIfPossibleArg = rejectIfPossible.asRubyArgument(name: "reject_if_possible", type: nil)
|
799
802
|
let automaticReleaseArg = automaticRelease.asRubyArgument(name: "automatic_release", type: nil)
|
@@ -856,6 +859,7 @@ public func appstore(apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault
|
|
856
859
|
skipAppVersionUpdateArg,
|
857
860
|
forceArg,
|
858
861
|
overwriteScreenshotsArg,
|
862
|
+
syncScreenshotsArg,
|
859
863
|
submitForReviewArg,
|
860
864
|
rejectIfPossibleArg,
|
861
865
|
automaticReleaseArg,
|
@@ -3668,6 +3672,7 @@ public func deleteKeychain(name: OptionalConfigValue<String?> = .fastlaneDefault
|
|
3668
3672
|
- skipAppVersionUpdate: Don’t create or update the app version that is being prepared for submission
|
3669
3673
|
- force: Skip verification of HTML preview file
|
3670
3674
|
- overwriteScreenshots: Clear all previously uploaded screenshots before uploading the new ones
|
3675
|
+
- syncScreenshots: Sync screenshots with local ones. This is currently beta optionso set true to 'FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS' environment variable as well
|
3671
3676
|
- submitForReview: Submit the new version for Review after uploading everything
|
3672
3677
|
- rejectIfPossible: Rejects the previously submitted build if it's in a state where it's possible
|
3673
3678
|
- automaticRelease: Should the app be automatically released once it's approved? (Can not be used together with `auto_release_date`)
|
@@ -3738,6 +3743,7 @@ public func deliver(apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault(
|
|
3738
3743
|
skipAppVersionUpdate: OptionalConfigValue<Bool> = .fastlaneDefault(deliverfile.skipAppVersionUpdate),
|
3739
3744
|
force: OptionalConfigValue<Bool> = .fastlaneDefault(deliverfile.force),
|
3740
3745
|
overwriteScreenshots: OptionalConfigValue<Bool> = .fastlaneDefault(deliverfile.overwriteScreenshots),
|
3746
|
+
syncScreenshots: OptionalConfigValue<Bool> = .fastlaneDefault(deliverfile.syncScreenshots),
|
3741
3747
|
submitForReview: OptionalConfigValue<Bool> = .fastlaneDefault(deliverfile.submitForReview),
|
3742
3748
|
rejectIfPossible: OptionalConfigValue<Bool> = .fastlaneDefault(deliverfile.rejectIfPossible),
|
3743
3749
|
automaticRelease: OptionalConfigValue<Bool?> = .fastlaneDefault(deliverfile.automaticRelease),
|
@@ -3801,6 +3807,7 @@ public func deliver(apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault(
|
|
3801
3807
|
let skipAppVersionUpdateArg = skipAppVersionUpdate.asRubyArgument(name: "skip_app_version_update", type: nil)
|
3802
3808
|
let forceArg = force.asRubyArgument(name: "force", type: nil)
|
3803
3809
|
let overwriteScreenshotsArg = overwriteScreenshots.asRubyArgument(name: "overwrite_screenshots", type: nil)
|
3810
|
+
let syncScreenshotsArg = syncScreenshots.asRubyArgument(name: "sync_screenshots", type: nil)
|
3804
3811
|
let submitForReviewArg = submitForReview.asRubyArgument(name: "submit_for_review", type: nil)
|
3805
3812
|
let rejectIfPossibleArg = rejectIfPossible.asRubyArgument(name: "reject_if_possible", type: nil)
|
3806
3813
|
let automaticReleaseArg = automaticRelease.asRubyArgument(name: "automatic_release", type: nil)
|
@@ -3863,6 +3870,7 @@ public func deliver(apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault(
|
|
3863
3870
|
skipAppVersionUpdateArg,
|
3864
3871
|
forceArg,
|
3865
3872
|
overwriteScreenshotsArg,
|
3873
|
+
syncScreenshotsArg,
|
3866
3874
|
submitForReviewArg,
|
3867
3875
|
rejectIfPossibleArg,
|
3868
3876
|
automaticReleaseArg,
|
@@ -12004,6 +12012,7 @@ public func uploadSymbolsToSentry(apiHost: String = "https://app.getsentry.com/a
|
|
12004
12012
|
- skipAppVersionUpdate: Don’t create or update the app version that is being prepared for submission
|
12005
12013
|
- force: Skip verification of HTML preview file
|
12006
12014
|
- overwriteScreenshots: Clear all previously uploaded screenshots before uploading the new ones
|
12015
|
+
- syncScreenshots: Sync screenshots with local ones. This is currently beta optionso set true to 'FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS' environment variable as well
|
12007
12016
|
- submitForReview: Submit the new version for Review after uploading everything
|
12008
12017
|
- rejectIfPossible: Rejects the previously submitted build if it's in a state where it's possible
|
12009
12018
|
- automaticRelease: Should the app be automatically released once it's approved? (Can not be used together with `auto_release_date`)
|
@@ -12074,6 +12083,7 @@ public func uploadToAppStore(apiKeyPath: OptionalConfigValue<String?> = .fastlan
|
|
12074
12083
|
skipAppVersionUpdate: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
12075
12084
|
force: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
12076
12085
|
overwriteScreenshots: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
12086
|
+
syncScreenshots: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
12077
12087
|
submitForReview: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
12078
12088
|
rejectIfPossible: OptionalConfigValue<Bool> = .fastlaneDefault(false),
|
12079
12089
|
automaticRelease: OptionalConfigValue<Bool?> = .fastlaneDefault(nil),
|
@@ -12137,6 +12147,7 @@ public func uploadToAppStore(apiKeyPath: OptionalConfigValue<String?> = .fastlan
|
|
12137
12147
|
let skipAppVersionUpdateArg = skipAppVersionUpdate.asRubyArgument(name: "skip_app_version_update", type: nil)
|
12138
12148
|
let forceArg = force.asRubyArgument(name: "force", type: nil)
|
12139
12149
|
let overwriteScreenshotsArg = overwriteScreenshots.asRubyArgument(name: "overwrite_screenshots", type: nil)
|
12150
|
+
let syncScreenshotsArg = syncScreenshots.asRubyArgument(name: "sync_screenshots", type: nil)
|
12140
12151
|
let submitForReviewArg = submitForReview.asRubyArgument(name: "submit_for_review", type: nil)
|
12141
12152
|
let rejectIfPossibleArg = rejectIfPossible.asRubyArgument(name: "reject_if_possible", type: nil)
|
12142
12153
|
let automaticReleaseArg = automaticRelease.asRubyArgument(name: "automatic_release", type: nil)
|
@@ -12199,6 +12210,7 @@ public func uploadToAppStore(apiKeyPath: OptionalConfigValue<String?> = .fastlan
|
|
12199
12210
|
skipAppVersionUpdateArg,
|
12200
12211
|
forceArg,
|
12201
12212
|
overwriteScreenshotsArg,
|
12213
|
+
syncScreenshotsArg,
|
12202
12214
|
submitForReviewArg,
|
12203
12215
|
rejectIfPossibleArg,
|
12204
12216
|
automaticReleaseArg,
|
@@ -13201,4 +13213,4 @@ public let snapshotfile = Snapshotfile()
|
|
13201
13213
|
|
13202
13214
|
// Please don't remove the lines below
|
13203
13215
|
// They are used to detect outdated files
|
13204
|
-
// FastlaneRunnerAPIVersion [0.9.
|
13216
|
+
// FastlaneRunnerAPIVersion [0.9.132]
|
@@ -51,9 +51,9 @@
|
|
51
51
|
"macOS": "11.0"
|
52
52
|
},
|
53
53
|
"monterey": {
|
54
|
-
"HOMEBREW_VERSION": "3.2.
|
54
|
+
"HOMEBREW_VERSION": "3.2.6-34-g6bb3699",
|
55
55
|
"HOMEBREW_PREFIX": "/usr/local",
|
56
|
-
"Homebrew/homebrew-core": "
|
56
|
+
"Homebrew/homebrew-core": "b7523de28df0f0f819ff2c49c84611eec19f5455",
|
57
57
|
"CLT": "13.0.0.0.1.1626155413",
|
58
58
|
"Xcode": "13.0",
|
59
59
|
"macOS": "12.0"
|
@@ -3,6 +3,7 @@ require 'commander'
|
|
3
3
|
require 'fastlane/version'
|
4
4
|
require 'fastlane_core/ui/help_formatter'
|
5
5
|
require 'fastlane_core/configuration/config_item'
|
6
|
+
require 'fastlane_core/print_table'
|
6
7
|
require_relative 'module'
|
7
8
|
require_relative 'manager'
|
8
9
|
require_relative 'options'
|
@@ -196,6 +197,33 @@ module Produce
|
|
196
197
|
end
|
197
198
|
end
|
198
199
|
|
200
|
+
command :available_services do |c|
|
201
|
+
c.syntax = 'fastlane produce available_services -a APP_IDENTIFIER'
|
202
|
+
c.description = 'Displays a list of allowed Application Services for a specific app.'
|
203
|
+
c.example('Check Available Services', 'fastlane produce available_services -a com.example.app')
|
204
|
+
|
205
|
+
FastlaneCore::CommanderGenerator.new.generate(Produce::Options.available_options, command: c)
|
206
|
+
|
207
|
+
c.action do |args, options|
|
208
|
+
# Filter the options so that we can still build the configuration
|
209
|
+
allowed_keys = Produce::Options.available_options.collect(&:key)
|
210
|
+
Produce.config = FastlaneCore::Configuration.create(Produce::Options.available_options, options.__hash__.select { |key, value| allowed_keys.include?(key) })
|
211
|
+
|
212
|
+
require 'produce/service'
|
213
|
+
require 'terminal-table'
|
214
|
+
|
215
|
+
services = Produce::Service.available_services(options, args)
|
216
|
+
rows = services.map { |capabilities| [capabilities.name, capabilities.id, capabilities.description] }
|
217
|
+
table = Terminal::Table.new(
|
218
|
+
title: "Available Services",
|
219
|
+
headings: ['Name', 'ID', 'Description'],
|
220
|
+
rows: FastlaneCore::PrintTable.transform_output(rows),
|
221
|
+
style: { all_separators: true }
|
222
|
+
)
|
223
|
+
puts(table)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
199
227
|
command :group do |c|
|
200
228
|
c.syntax = 'fastlane produce group'
|
201
229
|
c.description = 'Ensure that a specific App Group exists'
|
@@ -16,6 +16,10 @@ module Produce
|
|
16
16
|
self.new.disable(options, args)
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.available_services(options, args)
|
20
|
+
self.new.available_services(options, args)
|
21
|
+
end
|
22
|
+
|
19
23
|
def enable(options, _args)
|
20
24
|
unless bundle_id
|
21
25
|
UI.message("[DevCenter] App '#{Produce.config[:app_identifier]}' does not exist")
|
@@ -40,6 +44,17 @@ module Produce
|
|
40
44
|
UI.success("Done! Disabled #{disabled} services.")
|
41
45
|
end
|
42
46
|
|
47
|
+
def available_services(options, _args)
|
48
|
+
unless bundle_id
|
49
|
+
UI.message("[DevCenter] App '#{Produce.config[:app_identifier]}' does not exist")
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
UI.success("[DevCenter] App found '#{bundle_id.name}'")
|
54
|
+
UI.message("Fetching available services")
|
55
|
+
return Spaceship::ConnectAPI::Capabilities.all
|
56
|
+
end
|
57
|
+
|
43
58
|
def valid_services_for(options)
|
44
59
|
allowed_keys = [:access_wifi, :app_attest, :app_group, :apple_pay, :associated_domains, :auto_fill_credential, :car_play_audio_app, :car_play_messaging_app,
|
45
60
|
:car_play_navigation_app, :car_play_voip_calling_app, :class_kit, :icloud, :critical_alerts, :custom_network_protocol, :data_protection,
|
@@ -10,6 +10,7 @@ require 'spaceship/connect_api/tunes/tunes'
|
|
10
10
|
|
11
11
|
require 'spaceship/connect_api/models/bundle_id_capability'
|
12
12
|
require 'spaceship/connect_api/models/bundle_id'
|
13
|
+
require 'spaceship/connect_api/models/capabilities'
|
13
14
|
require 'spaceship/connect_api/models/certificate'
|
14
15
|
require 'spaceship/connect_api/models/device'
|
15
16
|
require 'spaceship/connect_api/models/profile'
|
@@ -423,6 +423,13 @@ module Spaceship
|
|
423
423
|
client.add_user_visible_apps(user_id: user_id, app_ids: [id])
|
424
424
|
end
|
425
425
|
end
|
426
|
+
|
427
|
+
def remove_users(client: nil, user_ids: nil)
|
428
|
+
client ||= Spaceship::ConnectAPI
|
429
|
+
user_ids.each do |user_id|
|
430
|
+
client.delete_user_visible_apps(user_id: user_id, app_ids: [id])
|
431
|
+
end
|
432
|
+
end
|
426
433
|
end
|
427
434
|
end
|
428
435
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative '../model'
|
2
|
+
|
3
|
+
module Spaceship
|
4
|
+
class ConnectAPI
|
5
|
+
class Capabilities
|
6
|
+
include Spaceship::ConnectAPI::Model
|
7
|
+
|
8
|
+
attr_accessor :name
|
9
|
+
attr_accessor :description
|
10
|
+
|
11
|
+
attr_mapping({
|
12
|
+
"name" => "name",
|
13
|
+
"description" => "description",
|
14
|
+
})
|
15
|
+
|
16
|
+
def self.type
|
17
|
+
return "capabilities"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.all(client: nil)
|
21
|
+
client ||= Spaceship::ConnectAPI
|
22
|
+
resp = client.get_available_bundle_id_capabilities(bundle_id_id: id).all_pages
|
23
|
+
return resp.flat_map(&:to_models)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -54,6 +54,11 @@ module Spaceship
|
|
54
54
|
provisioning_request_client.get("bundleIds/#{bundle_id_id}/bundleIdCapabilities", params)
|
55
55
|
end
|
56
56
|
|
57
|
+
def get_available_bundle_id_capabilities(bundle_id_id:)
|
58
|
+
params = provisioning_request_client.build_params(filter: { bundleId: bundle_id_id })
|
59
|
+
provisioning_request_client.get("capabilities", params)
|
60
|
+
end
|
61
|
+
|
57
62
|
def post_bundle_id_capability(bundle_id_id:, capability_type:, settings: [])
|
58
63
|
body = {
|
59
64
|
data: {
|
@@ -28,8 +28,13 @@ module Spaceship
|
|
28
28
|
users_request_client.delete("users/#{user_id}")
|
29
29
|
end
|
30
30
|
|
31
|
-
#
|
31
|
+
# Add app permissions for user
|
32
|
+
# @deprecated Use {#post_user_visible_apps} instead.
|
32
33
|
def add_user_visible_apps(user_id: nil, app_ids: nil)
|
34
|
+
post_user_visible_apps(user_id: user_id, app_ids: app_ids)
|
35
|
+
end
|
36
|
+
|
37
|
+
def post_user_visible_apps(user_id: nil, app_ids: nil)
|
33
38
|
body = {
|
34
39
|
data: app_ids.map do |app_id|
|
35
40
|
{
|
@@ -42,6 +47,34 @@ module Spaceship
|
|
42
47
|
users_request_client.post("users/#{user_id}/relationships/visibleApps", body)
|
43
48
|
end
|
44
49
|
|
50
|
+
# Replace app permissions for user
|
51
|
+
def patch_user_visible_apps(user_id: nil, app_ids: nil)
|
52
|
+
body = {
|
53
|
+
data: app_ids.map do |app_id|
|
54
|
+
{
|
55
|
+
type: "apps",
|
56
|
+
id: app_id
|
57
|
+
}
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
users_request_client.patch("users/#{user_id}/relationships/visibleApps", body)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Remove app permissions for user
|
65
|
+
def delete_user_visible_apps(user_id: nil, app_ids: nil)
|
66
|
+
body = {
|
67
|
+
data: app_ids.map do |app_id|
|
68
|
+
{
|
69
|
+
type: "apps",
|
70
|
+
id: app_id
|
71
|
+
}
|
72
|
+
end
|
73
|
+
}
|
74
|
+
params = nil
|
75
|
+
users_request_client.delete("users/#{user_id}/relationships/visibleApps", params, body)
|
76
|
+
end
|
77
|
+
|
45
78
|
# Get app permissions for user
|
46
79
|
def get_user_visible_apps(user_id: id, limit: nil)
|
47
80
|
params = users_request_client.build_params(filter: {}, includes: nil, limit: limit, sort: nil)
|
@@ -302,7 +302,7 @@ module Supply
|
|
302
302
|
|
303
303
|
def upload_mapping(apk_version_codes)
|
304
304
|
mapping_paths = [Supply.config[:mapping]] unless (mapping_paths = Supply.config[:mapping_paths])
|
305
|
-
mapping_paths.
|
305
|
+
mapping_paths.product(apk_version_codes).each do |mapping_path, version_code|
|
306
306
|
if mapping_path
|
307
307
|
UI.message("Preparing mapping at path '#{mapping_path}', version code #{version_code} for upload...")
|
308
308
|
client.upload_mapping(mapping_path, version_code)
|
metadata
CHANGED
@@ -1,38 +1,38 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.191.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Andrew McBurney
|
8
|
+
- Fumiya Nakamura
|
9
|
+
- Manu Wallner
|
10
|
+
- Jimmy Dee
|
11
|
+
- Satoshi Namai
|
12
|
+
- Josh Holtz
|
13
|
+
- Felix Krause
|
8
14
|
- Jérôme Lacoste
|
9
|
-
-
|
10
|
-
- Daniel Jankowski
|
11
|
-
- Stefan Natchev
|
12
|
-
- Luka Mirosevic
|
13
|
-
- Jorge Revuelta H
|
15
|
+
- Manish Rathi
|
14
16
|
- Kohki Miki
|
15
|
-
-
|
16
|
-
- Joshua Liebowitz
|
17
|
+
- Iulian Onofrei
|
17
18
|
- Matthew Ellis
|
18
|
-
-
|
19
|
-
- Josh Holtz
|
20
|
-
- Jimmy Dee
|
21
|
-
- Satoshi Namai
|
19
|
+
- Aaron Brager
|
22
20
|
- Danielle Tomlinson
|
23
|
-
-
|
21
|
+
- Max Ott
|
22
|
+
- Roger Oba
|
24
23
|
- Olivier Halligon
|
25
|
-
-
|
24
|
+
- Maksym Grebenets
|
26
25
|
- Jan Piotrowski
|
27
|
-
-
|
28
|
-
-
|
29
|
-
-
|
30
|
-
-
|
31
|
-
-
|
26
|
+
- Jorge Revuelta H
|
27
|
+
- Luka Mirosevic
|
28
|
+
- Daniel Jankowski
|
29
|
+
- Stefan Natchev
|
30
|
+
- Helmut Januschka
|
31
|
+
- Joshua Liebowitz
|
32
32
|
autorequire:
|
33
33
|
bindir: bin
|
34
34
|
cert_chain: []
|
35
|
-
date: 2021-08-
|
35
|
+
date: 2021-08-06 00:00:00.000000000 Z
|
36
36
|
dependencies:
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: xcodeproj
|
@@ -971,8 +971,10 @@ files:
|
|
971
971
|
- deliver/lib/deliver/module.rb
|
972
972
|
- deliver/lib/deliver/options.rb
|
973
973
|
- deliver/lib/deliver/runner.rb
|
974
|
+
- deliver/lib/deliver/screenshot_comparable.rb
|
974
975
|
- deliver/lib/deliver/setup.rb
|
975
976
|
- deliver/lib/deliver/submit_for_review.rb
|
977
|
+
- deliver/lib/deliver/sync_screenshots.rb
|
976
978
|
- deliver/lib/deliver/upload_metadata.rb
|
977
979
|
- deliver/lib/deliver/upload_price_tier.rb
|
978
980
|
- deliver/lib/deliver/upload_screenshots.rb
|
@@ -1680,6 +1682,7 @@ files:
|
|
1680
1682
|
- spaceship/lib/spaceship/connect_api/models/build_delivery.rb
|
1681
1683
|
- spaceship/lib/spaceship/connect_api/models/bundle_id.rb
|
1682
1684
|
- spaceship/lib/spaceship/connect_api/models/bundle_id_capability.rb
|
1685
|
+
- spaceship/lib/spaceship/connect_api/models/capabilities.rb
|
1683
1686
|
- spaceship/lib/spaceship/connect_api/models/certificate.rb
|
1684
1687
|
- spaceship/lib/spaceship/connect_api/models/custom_app_organization.rb
|
1685
1688
|
- spaceship/lib/spaceship/connect_api/models/custom_app_user.rb
|