fastlane 2.232.2 → 2.233.0
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.
- checksums.yaml +4 -4
- data/README.md +98 -98
- data/credentials_manager/lib/credentials_manager/appfile_config.rb +4 -0
- data/deliver/lib/deliver/detect_values.rb +2 -0
- data/deliver/lib/deliver/options.rb +23 -0
- data/deliver/lib/deliver/runner.rb +17 -12
- data/deliver/lib/deliver/sync_app_previews.rb +204 -0
- data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +5 -1
- data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +20 -4
- data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +3 -0
- data/fastlane/lib/fastlane/actions/resign.rb +13 -2
- data/fastlane/lib/fastlane/actions/swiftlint.rb +8 -1
- data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +1 -1
- data/fastlane/lib/fastlane/helper/s3_client_helper.rb +5 -2
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +29 -1
- data/fastlane/swift/Fastlane.swift +105 -9
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +8 -1
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +8 -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/itunes_transporter.rb +48 -17
- data/fastlane_core/lib/fastlane_core/video_utils.rb +202 -0
- data/frameit/lib/frameit/device_types.rb +2 -2
- data/gym/lib/gym/generators/build_command_generator.rb +2 -1
- data/gym/lib/gym/options.rb +5 -0
- data/match/lib/match/generator.rb +3 -1
- data/match/lib/match/options.rb +5 -0
- data/match/lib/match/runner.rb +12 -7
- data/match/lib/match/storage/s3_storage.rb +4 -1
- data/match/lib/match/storage.rb +1 -0
- data/pilot/lib/pilot/build_manager.rb +4 -12
- data/pilot/lib/pilot/options.rb +4 -0
- data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/README.md +54 -0
- data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/en_us.txt +2 -1
- data/scan/lib/scan/detect_values.rb +11 -3
- data/sigh/lib/assets/resign.sh +17 -5
- data/sigh/lib/sigh/commands_generator.rb +1 -0
- data/sigh/lib/sigh/manager.rb +6 -6
- data/sigh/lib/sigh/resign.rb +9 -6
- data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +54 -17
- data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +1 -2
- data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +2 -2
- data/trainer/lib/trainer/legacy_xcresult.rb +27 -20
- data/trainer/lib/trainer/xcresult/test_suite.rb +4 -1
- metadata +25 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a1e9ccdeab76489550801ba5eca3943ce3fb8b0f6a54e68281f39879469b5476
|
|
4
|
+
data.tar.gz: abd22788dfc2496906517916afbc50d18ae7789e5ba8430ccfdef5b979573f2b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a801932f673957a2ce012624ad221efd34d7d0d04deca0f7df0f6dbc7eb976bb5ad1eaf6e0d005e1e60c7e01219665afa0b514e695dc063cf75e2834e79f0aaf
|
|
7
|
+
data.tar.gz: 81ff84ead2d0339f588af6fae06452092a97dac684513c5f5b02af084084bd727a6b140d17d01f3d85ed1f6d374f7063828dc6a86aa5ba139d0acbdd36ccb799
|
data/README.md
CHANGED
|
@@ -35,55 +35,55 @@ 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='manu-wallner'>
|
|
39
|
+
<a href='https://github.com/milch'>
|
|
40
|
+
<img src='https://github.com/milch.png' width='140px;'>
|
|
41
41
|
</a>
|
|
42
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
42
|
+
<h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
|
|
43
43
|
</td>
|
|
44
|
-
<td id='
|
|
45
|
-
<a href='https://github.com/
|
|
46
|
-
<img src='https://github.com/
|
|
44
|
+
<td id='danielle-tomlinson'>
|
|
45
|
+
<a href='https://github.com/endocrimes'>
|
|
46
|
+
<img src='https://github.com/endocrimes.png' width='140px;'>
|
|
47
47
|
</a>
|
|
48
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
48
|
+
<h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
|
|
49
49
|
</td>
|
|
50
|
-
<td id='
|
|
51
|
-
<a href='https://github.com/
|
|
52
|
-
<img src='https://github.com/
|
|
50
|
+
<td id='aaron-brager'>
|
|
51
|
+
<a href='https://github.com/getaaron'>
|
|
52
|
+
<img src='https://github.com/getaaron.png' width='140px;'>
|
|
53
53
|
</a>
|
|
54
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
54
|
+
<h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
|
|
55
55
|
</td>
|
|
56
|
-
<td id='
|
|
57
|
-
<a href='https://github.com/
|
|
58
|
-
<img src='https://github.com/
|
|
56
|
+
<td id='jimmy-dee'>
|
|
57
|
+
<a href='https://github.com/jdee'>
|
|
58
|
+
<img src='https://github.com/jdee.png' width='140px;'>
|
|
59
59
|
</a>
|
|
60
|
-
<h4 align='center'
|
|
60
|
+
<h4 align='center'>Jimmy Dee</h4>
|
|
61
61
|
</td>
|
|
62
|
-
<td id='
|
|
63
|
-
<a href='https://github.com/
|
|
64
|
-
<img src='https://github.com/
|
|
62
|
+
<td id='kohki-miki'>
|
|
63
|
+
<a href='https://github.com/giginet'>
|
|
64
|
+
<img src='https://github.com/giginet.png' width='140px;'>
|
|
65
65
|
</a>
|
|
66
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
66
|
+
<h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
|
|
67
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='matthew-ellis'>
|
|
71
|
+
<a href='https://github.com/matthewellis'>
|
|
72
|
+
<img src='https://github.com/matthewellis.png' width='140px;'>
|
|
73
73
|
</a>
|
|
74
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
74
|
+
<h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
|
|
75
75
|
</td>
|
|
76
|
-
<td id='
|
|
77
|
-
<a href='https://github.com/
|
|
78
|
-
<img src='https://github.com/
|
|
76
|
+
<td id='fumiya-nakamura'>
|
|
77
|
+
<a href='https://github.com/nafu'>
|
|
78
|
+
<img src='https://github.com/nafu.png' width='140px;'>
|
|
79
79
|
</a>
|
|
80
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
80
|
+
<h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
|
|
81
81
|
</td>
|
|
82
|
-
<td id='
|
|
83
|
-
<a href='https://github.com/
|
|
84
|
-
<img src='https://github.com/
|
|
82
|
+
<td id='luka-mirosevic'>
|
|
83
|
+
<a href='https://github.com/lmirosevic'>
|
|
84
|
+
<img src='https://github.com/lmirosevic.png' width='140px;'>
|
|
85
85
|
</a>
|
|
86
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
86
|
+
<h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
|
|
87
87
|
</td>
|
|
88
88
|
<td id='josh-holtz'>
|
|
89
89
|
<a href='https://github.com/joshdholtz'>
|
|
@@ -91,89 +91,89 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
91
91
|
</a>
|
|
92
92
|
<h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
|
|
93
93
|
</td>
|
|
94
|
-
<td id='
|
|
95
|
-
<a href='https://github.com/
|
|
96
|
-
<img src='https://github.com/
|
|
94
|
+
<td id='jérôme-lacoste'>
|
|
95
|
+
<a href='https://github.com/lacostej'>
|
|
96
|
+
<img src='https://github.com/lacostej.png' width='140px;'>
|
|
97
97
|
</a>
|
|
98
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
98
|
+
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
|
99
99
|
</td>
|
|
100
100
|
</tr>
|
|
101
101
|
<tr>
|
|
102
|
-
<td id='andrew-mcburney'>
|
|
103
|
-
<a href='https://github.com/armcburney'>
|
|
104
|
-
<img src='https://github.com/armcburney.png' width='140px;'>
|
|
105
|
-
</a>
|
|
106
|
-
<h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
|
|
107
|
-
</td>
|
|
108
102
|
<td id='daniel-jankowski'>
|
|
109
103
|
<a href='https://github.com/mollyIV'>
|
|
110
104
|
<img src='https://github.com/mollyIV.png' width='140px;'>
|
|
111
105
|
</a>
|
|
112
106
|
<h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
|
|
113
107
|
</td>
|
|
114
|
-
<td id='
|
|
115
|
-
<a href='https://github.com/
|
|
116
|
-
<img src='https://github.com/
|
|
108
|
+
<td id='helmut-januschka'>
|
|
109
|
+
<a href='https://github.com/hjanuschka'>
|
|
110
|
+
<img src='https://github.com/hjanuschka.png' width='140px;'>
|
|
117
111
|
</a>
|
|
118
|
-
<h4 align='center'
|
|
112
|
+
<h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
|
|
119
113
|
</td>
|
|
120
|
-
<td id='
|
|
121
|
-
<a href='https://github.com/
|
|
122
|
-
<img src='https://github.com/
|
|
114
|
+
<td id='andrew-mcburney'>
|
|
115
|
+
<a href='https://github.com/armcburney'>
|
|
116
|
+
<img src='https://github.com/armcburney.png' width='140px;'>
|
|
123
117
|
</a>
|
|
124
|
-
<h4 align='center'>
|
|
118
|
+
<h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
|
|
125
119
|
</td>
|
|
126
|
-
<td id='
|
|
127
|
-
<a href='https://github.com/
|
|
128
|
-
<img src='https://github.com/
|
|
120
|
+
<td id='iulian-onofrei'>
|
|
121
|
+
<a href='https://github.com/revolter'>
|
|
122
|
+
<img src='https://github.com/revolter.png' width='140px;'>
|
|
129
123
|
</a>
|
|
130
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
124
|
+
<h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
|
|
125
|
+
</td>
|
|
126
|
+
<td id='jorge-revuelta-h'>
|
|
127
|
+
<a href='https://github.com/minuscorp'>
|
|
128
|
+
<img src='https://github.com/minuscorp.png' width='140px;'>
|
|
129
|
+
</a>
|
|
130
|
+
<h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</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='connor-tumbleson'>
|
|
135
|
+
<a href='https://github.com/ibotpeaches'>
|
|
136
|
+
<img src='https://github.com/ibotpeaches.png' width='140px;'>
|
|
137
137
|
</a>
|
|
138
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
138
|
+
<h4 align='center'><a href='https://twitter.com/ibotpeaches'>Connor Tumbleson</a></h4>
|
|
139
139
|
</td>
|
|
140
|
-
<td id='
|
|
141
|
-
<a href='https://github.com/
|
|
142
|
-
<img src='https://github.com/
|
|
140
|
+
<td id='jan-piotrowski'>
|
|
141
|
+
<a href='https://github.com/janpio'>
|
|
142
|
+
<img src='https://github.com/janpio.png' width='140px;'>
|
|
143
143
|
</a>
|
|
144
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
144
|
+
<h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
|
|
145
145
|
</td>
|
|
146
|
-
<td id='
|
|
147
|
-
<a href='https://github.com/
|
|
148
|
-
<img src='https://github.com/
|
|
146
|
+
<td id='stefan-natchev'>
|
|
147
|
+
<a href='https://github.com/snatchev'>
|
|
148
|
+
<img src='https://github.com/snatchev.png' width='140px;'>
|
|
149
149
|
</a>
|
|
150
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
150
|
+
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
|
151
151
|
</td>
|
|
152
|
-
<td id='
|
|
153
|
-
<a href='https://github.com/
|
|
154
|
-
<img src='https://github.com/
|
|
152
|
+
<td id='manish-rathi'>
|
|
153
|
+
<a href='https://github.com/crazymanish'>
|
|
154
|
+
<img src='https://github.com/crazymanish.png' width='140px;'>
|
|
155
155
|
</a>
|
|
156
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
156
|
+
<h4 align='center'><a href='https://twitter.com/iammanishrathi'>Manish Rathi</a></h4>
|
|
157
157
|
</td>
|
|
158
|
-
<td id='
|
|
159
|
-
<a href='https://github.com/
|
|
160
|
-
<img src='https://github.com/
|
|
158
|
+
<td id='olivier-halligon'>
|
|
159
|
+
<a href='https://github.com/AliSoftware'>
|
|
160
|
+
<img src='https://github.com/AliSoftware.png' width='140px;'>
|
|
161
161
|
</a>
|
|
162
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
162
|
+
<h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
|
|
163
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='roger-oba'>
|
|
167
|
+
<a href='https://github.com/rogerluan'>
|
|
168
|
+
<img src='https://github.com/rogerluan.png' width='140px;'>
|
|
169
169
|
</a>
|
|
170
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
170
|
+
<h4 align='center'><a href='https://twitter.com/rogerluan_'>Roger Oba</a></h4>
|
|
171
171
|
</td>
|
|
172
|
-
<td id='
|
|
173
|
-
<a href='https://github.com/
|
|
174
|
-
<img src='https://github.com/
|
|
172
|
+
<td id='maksym-grebenets'>
|
|
173
|
+
<a href='https://github.com/mgrebenets'>
|
|
174
|
+
<img src='https://github.com/mgrebenets.png' width='140px;'>
|
|
175
175
|
</a>
|
|
176
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
176
|
+
<h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
|
|
177
177
|
</td>
|
|
178
178
|
<td id='felix-krause'>
|
|
179
179
|
<a href='https://github.com/KrauseFx'>
|
|
@@ -181,31 +181,31 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
181
181
|
</a>
|
|
182
182
|
<h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
|
|
183
183
|
</td>
|
|
184
|
-
<td id='
|
|
185
|
-
<a href='https://github.com/
|
|
186
|
-
<img src='https://github.com/
|
|
184
|
+
<td id='łukasz-grabowski'>
|
|
185
|
+
<a href='https://github.com/lucgrabowski'>
|
|
186
|
+
<img src='https://github.com/lucgrabowski.png' width='140px;'>
|
|
187
187
|
</a>
|
|
188
|
-
<h4 align='center'
|
|
188
|
+
<h4 align='center'>Łukasz Grabowski</h4>
|
|
189
189
|
</td>
|
|
190
|
-
<td id='
|
|
191
|
-
<a href='https://github.com/
|
|
192
|
-
<img src='https://github.com/
|
|
190
|
+
<td id='joshua-liebowitz'>
|
|
191
|
+
<a href='https://github.com/taquitos'>
|
|
192
|
+
<img src='https://github.com/taquitos.png' width='140px;'>
|
|
193
193
|
</a>
|
|
194
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
194
|
+
<h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
|
|
195
195
|
</td>
|
|
196
196
|
</tr>
|
|
197
197
|
<tr>
|
|
198
|
-
<td id='
|
|
199
|
-
<a href='https://github.com/
|
|
200
|
-
<img src='https://github.com/
|
|
198
|
+
<td id='satoshi-namai'>
|
|
199
|
+
<a href='https://github.com/ainame'>
|
|
200
|
+
<img src='https://github.com/ainame.png' width='140px;'>
|
|
201
201
|
</a>
|
|
202
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
202
|
+
<h4 align='center'><a href='https://twitter.com/ainame'>Satoshi Namai</a></h4>
|
|
203
203
|
</td>
|
|
204
|
-
<td id='
|
|
205
|
-
<a href='https://github.com/
|
|
206
|
-
<img src='https://github.com/
|
|
204
|
+
<td id='max-ott'>
|
|
205
|
+
<a href='https://github.com/max-ott'>
|
|
206
|
+
<img src='https://github.com/max-ott.png' width='140px;'>
|
|
207
207
|
</a>
|
|
208
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
208
|
+
<h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
|
|
209
209
|
</td>
|
|
210
210
|
</table>
|
|
211
211
|
|
|
@@ -123,6 +123,10 @@ module CredentialsManager
|
|
|
123
123
|
setter(:itc_provider, *args, &block)
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
+
def provider_public_id(*args, &block)
|
|
127
|
+
setter(:provider_public_id, *args, &block)
|
|
128
|
+
end
|
|
129
|
+
|
|
126
130
|
# Android
|
|
127
131
|
def json_key_file(*args, &block)
|
|
128
132
|
setter(:json_key_file, *args, &block)
|
|
@@ -58,11 +58,13 @@ module Deliver
|
|
|
58
58
|
def find_folders(options)
|
|
59
59
|
containing = Helper.fastlane_enabled? ? FastlaneCore::FastlaneFolder.path : '.'
|
|
60
60
|
options[:screenshots_path] ||= File.join(containing, 'screenshots')
|
|
61
|
+
options[:app_previews_path] ||= File.join(containing, 'app-previews')
|
|
61
62
|
options[:metadata_path] ||= File.join(containing, 'metadata')
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def ensure_folders_created(options)
|
|
65
66
|
FileUtils.mkdir_p(options[:screenshots_path])
|
|
67
|
+
FileUtils.mkdir_p(options[:app_previews_path])
|
|
66
68
|
FileUtils.mkdir_p(options[:metadata_path])
|
|
67
69
|
end
|
|
68
70
|
|
|
@@ -128,6 +128,22 @@ module Deliver
|
|
|
128
128
|
description: "Path to the folder containing the screenshots",
|
|
129
129
|
optional: true),
|
|
130
130
|
|
|
131
|
+
# app previews (videos)
|
|
132
|
+
FastlaneCore::ConfigItem.new(key: :app_previews_path,
|
|
133
|
+
env_name: "DELIVER_APP_PREVIEWS_PATH",
|
|
134
|
+
description: "Path to the folder containing localized App Preview videos",
|
|
135
|
+
optional: true),
|
|
136
|
+
FastlaneCore::ConfigItem.new(key: :preview_frame_time_code,
|
|
137
|
+
env_name: "DELIVER_PREVIEW_FRAME_TIME_CODE",
|
|
138
|
+
description: "Time code for the App Preview still frame written as hour:minute:second:centisecond (e.g. 00:00:00:01)",
|
|
139
|
+
optional: true,
|
|
140
|
+
default_value: "00:00:05:00"),
|
|
141
|
+
FastlaneCore::ConfigItem.new(key: :overwrite_preview_videos,
|
|
142
|
+
env_name: "DELIVER_OVERWRITE_PREVIEW_VIDEOS",
|
|
143
|
+
description: "Clear all previously uploaded App Preview videos before uploading the new ones",
|
|
144
|
+
type: Boolean,
|
|
145
|
+
default_value: false),
|
|
146
|
+
|
|
131
147
|
# skip
|
|
132
148
|
FastlaneCore::ConfigItem.new(key: :skip_binary_upload,
|
|
133
149
|
env_name: "DELIVER_SKIP_BINARY_UPLOAD",
|
|
@@ -307,6 +323,13 @@ module Deliver
|
|
|
307
323
|
code_gen_sensitive: true,
|
|
308
324
|
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:itc_provider),
|
|
309
325
|
default_value_dynamic: true),
|
|
326
|
+
FastlaneCore::ConfigItem.new(key: :provider_public_id,
|
|
327
|
+
env_name: "DELIVER_PROVIDER_PUBLIC_ID",
|
|
328
|
+
description: "The provider public ID to be used with altool (--provider-public-id). This value will override the automatically detected provider value for altool uploads. Required after Xcode 26 when your account is associated with multiple providers and using username/app-password authentication",
|
|
329
|
+
optional: true,
|
|
330
|
+
code_gen_sensitive: true,
|
|
331
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:provider_public_id),
|
|
332
|
+
default_value_dynamic: true),
|
|
310
333
|
# rubocop:enable Layout/LineLength
|
|
311
334
|
|
|
312
335
|
# precheck
|
|
@@ -11,6 +11,7 @@ require_relative 'upload_price_tier'
|
|
|
11
11
|
require_relative 'upload_metadata'
|
|
12
12
|
require_relative 'upload_screenshots'
|
|
13
13
|
require_relative 'sync_screenshots'
|
|
14
|
+
require_relative 'sync_app_previews'
|
|
14
15
|
require_relative 'detect_values'
|
|
15
16
|
|
|
16
17
|
module Deliver
|
|
@@ -158,6 +159,17 @@ module Deliver
|
|
|
158
159
|
upload_screenshots.upload(options, screenshots)
|
|
159
160
|
end
|
|
160
161
|
|
|
162
|
+
if options[:app_previews_path]
|
|
163
|
+
previews = Deliver::SyncAppPreviews.new(
|
|
164
|
+
app: Deliver.cache[:app],
|
|
165
|
+
platform: Spaceship::ConnectAPI::Platform.map(options[:platform]),
|
|
166
|
+
app_previews_path: options[:app_previews_path],
|
|
167
|
+
preview_frame_time_code: options[:preview_frame_time_code],
|
|
168
|
+
overwrite_preview_videos: options[:overwrite_preview_videos]
|
|
169
|
+
)
|
|
170
|
+
previews.sync_from_path
|
|
171
|
+
end
|
|
172
|
+
|
|
161
173
|
UploadPriceTier.new.upload(options)
|
|
162
174
|
end
|
|
163
175
|
|
|
@@ -265,7 +277,7 @@ module Deliver
|
|
|
265
277
|
|
|
266
278
|
# If App Store Connect API token, use token.
|
|
267
279
|
# If api_key is specified and it is an Individual API Key, don't use token but use username.
|
|
268
|
-
# If itc_provider was explicitly specified, use it.
|
|
280
|
+
# If itc_provider or provider_public_id was explicitly specified, use it.
|
|
269
281
|
# If there are multiple teams, infer the provider from the selected team name.
|
|
270
282
|
# If there are fewer than two teams, don't infer the provider.
|
|
271
283
|
def transporter_for_selected_team
|
|
@@ -281,23 +293,16 @@ module Deliver
|
|
|
281
293
|
api_key
|
|
282
294
|
end
|
|
283
295
|
|
|
284
|
-
# Currently no kind of transporters accept an Individual API Key. Use username and app-specific password instead.
|
|
285
|
-
# See https://github.com/fastlane/fastlane/issues/22115
|
|
286
|
-
is_individual_key = !api_key.nil? && api_key[:issuer_id].nil?
|
|
287
|
-
if is_individual_key
|
|
288
|
-
api_key = nil
|
|
289
|
-
api_token = nil
|
|
290
|
-
end
|
|
291
|
-
|
|
292
296
|
unless api_token.nil?
|
|
293
297
|
api_token.refresh! if api_token.expired?
|
|
294
|
-
return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text, altool_compatible_command: true, api_key: api_key)
|
|
298
|
+
return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text, altool_compatible_command: true, api_key: api_key, provider_public_id: nil)
|
|
295
299
|
end
|
|
296
300
|
|
|
297
301
|
tunes_client = Spaceship::ConnectAPI.client.tunes_client
|
|
298
302
|
|
|
299
|
-
generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider], altool_compatible_command: true, api_key: api_key)
|
|
300
|
-
return generic_transporter
|
|
303
|
+
generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider], altool_compatible_command: true, api_key: api_key, provider_public_id: options[:provider_public_id])
|
|
304
|
+
return generic_transporter if options[:itc_provider] || options[:provider_public_id] || tunes_client.nil?
|
|
305
|
+
return generic_transporter unless tunes_client.teams.count > 1
|
|
301
306
|
|
|
302
307
|
begin
|
|
303
308
|
team = tunes_client.teams.find { |t| t['providerId'].to_s == tunes_client.team_id }
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require "fastlane_core"
|
|
2
|
+
require "fastlane_core/video_utils"
|
|
3
|
+
require "digest/md5"
|
|
4
|
+
|
|
5
|
+
require_relative 'module'
|
|
6
|
+
|
|
7
|
+
module Deliver
|
|
8
|
+
class SyncAppPreviews
|
|
9
|
+
UploadPreviewJob = Struct.new(:localization, :preview_set, :video_path, :frame_time_code)
|
|
10
|
+
|
|
11
|
+
def initialize(app:, platform:, app_previews_path:, preview_frame_time_code: nil, overwrite_preview_videos: false)
|
|
12
|
+
@app = app
|
|
13
|
+
@platform = platform
|
|
14
|
+
@app_previews_path = app_previews_path
|
|
15
|
+
@frame_time_code = preview_frame_time_code
|
|
16
|
+
@overwrite = overwrite_preview_videos
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def sync_from_path
|
|
20
|
+
UI.important("Uploading App Preview videos...")
|
|
21
|
+
validate_path!
|
|
22
|
+
|
|
23
|
+
localizations = editable_version.get_app_store_version_localizations
|
|
24
|
+
locale_by_code = localizations.each_with_object({}) { |l, h| h[l.locale] = l }
|
|
25
|
+
|
|
26
|
+
video_previews_per_locale = discover_videos(@app_previews_path)
|
|
27
|
+
if video_previews_per_locale.empty?
|
|
28
|
+
UI.message("No preview videos found under '#{@app_previews_path}'.")
|
|
29
|
+
return
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
jobs = []
|
|
33
|
+
video_previews_per_locale.each do |locale, previews|
|
|
34
|
+
process_locale_videos(locale: locale, previews: previews, locale_by_code: locale_by_code, jobs: jobs)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if jobs.empty?
|
|
38
|
+
UI.message("No new preview videos to upload.")
|
|
39
|
+
return
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
UI.message("Queueing #{jobs.size} preview video upload job(s) across #{video_previews_per_locale.keys.size} locale(s)...")
|
|
43
|
+
|
|
44
|
+
upload_errors = []
|
|
45
|
+
worker = FastlaneCore::QueueWorker.new do |job|
|
|
46
|
+
begin
|
|
47
|
+
UI.message("Uploading preview video '#{File.basename(job.video_path)}' for locale #{job.localization.locale} (set #{job.preview_set.preview_type})...")
|
|
48
|
+
job.preview_set.upload_preview(path: job.video_path, frame_time_code: job.frame_time_code)
|
|
49
|
+
UI.message("Uploaded preview video '#{File.basename(job.video_path)}' for locale #{job.localization.locale} (set #{job.preview_set.preview_type}).")
|
|
50
|
+
rescue => e
|
|
51
|
+
UI.error("Failed to upload '#{job.video_path}': #{e.class} - #{e.message}")
|
|
52
|
+
upload_errors << e
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
jobs.each { |j| worker.enqueue(j) }
|
|
57
|
+
worker.start
|
|
58
|
+
UI.message("All upload jobs finished. Sorting previews by filename...")
|
|
59
|
+
|
|
60
|
+
# sort previews in each set by file name
|
|
61
|
+
localizations.each do |loc|
|
|
62
|
+
loc.get_app_preview_sets(includes: "appPreviews").each do |set|
|
|
63
|
+
next unless set.app_previews && set.app_previews.length > 1
|
|
64
|
+
ordered_ids = set.app_previews.sort_by { |preview| preview.file_name.to_s }.map(&:id)
|
|
65
|
+
set.reorder_previews(app_preview_ids: ordered_ids)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
unless upload_errors.empty?
|
|
70
|
+
UI.user_error!("#{upload_errors.size} App Preview upload error(s) occurred. First error: #{upload_errors.first.class} - #{upload_errors.first.message}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
UI.success("Successfully uploaded and sorted App Preview videos.")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# process videos for a single locale: enforce limits, create/reuse sets, enqueue upload jobs
|
|
79
|
+
def process_locale_videos(locale:, previews:, locale_by_code:, jobs:)
|
|
80
|
+
localization = locale_by_code[locale]
|
|
81
|
+
unless localization
|
|
82
|
+
UI.important("Locale '#{locale}' does not exist on App Store Connect for this version. Skipping its videos.")
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
sets_by_preview_type = localization
|
|
87
|
+
.get_app_preview_sets(includes: "appPreviews")
|
|
88
|
+
.each_with_object({}) { |set, h| h[set.preview_type] = set }
|
|
89
|
+
|
|
90
|
+
if @overwrite
|
|
91
|
+
delete_existing_previews(localization, sets_by_preview_type.values)
|
|
92
|
+
# re-fetch sets after deletes
|
|
93
|
+
sets_by_preview_type = localization
|
|
94
|
+
.get_app_preview_sets(includes: "appPreviews")
|
|
95
|
+
.each_with_object({}) { |set, h| h[set.preview_type] = set }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# group videos by preview type to enforce a max of 3 per locale AND type
|
|
99
|
+
videos_by_preview_type = Hash.new { |h, k| h[k] = [] }
|
|
100
|
+
previews.each { |item| videos_by_preview_type[item[:preview_type]] << item[:path] }
|
|
101
|
+
|
|
102
|
+
videos_by_preview_type.each do |preview_type, video_paths|
|
|
103
|
+
video_paths.sort!
|
|
104
|
+
if video_paths.size > 3
|
|
105
|
+
UI.important("[#{locale}] Found #{video_paths.size} '#{preview_type}' videos. Limiting to first 3 by filename.")
|
|
106
|
+
video_paths = video_paths.first(3)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
preview_set = sets_by_preview_type[preview_type] || begin
|
|
110
|
+
UI.message("[#{locale}] Creating App Preview Set for type #{preview_type}...")
|
|
111
|
+
created = localization.create_app_preview_set(attributes: { previewType: preview_type })
|
|
112
|
+
sets_by_preview_type[preview_type] = created
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
video_paths.each do |video_path|
|
|
116
|
+
already_exist = (preview_set.app_previews || []).any? { |preview| preview.source_file_checksum == Digest::MD5.file(video_path).hexdigest }
|
|
117
|
+
if already_exist
|
|
118
|
+
UI.message("[#{locale}] Preview '#{File.basename(video_path)}' already uploaded (matching checksum). Skipping upload.")
|
|
119
|
+
next
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
jobs << UploadPreviewJob.new(localization, preview_set, video_path, @frame_time_code)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def validate_path!
|
|
128
|
+
UI.user_error!("app_previews_path is required") if @app_previews_path.to_s.empty?
|
|
129
|
+
UI.user_error!("app_previews_path '#{@app_previews_path}' does not exist") unless Dir.exist?(@app_previews_path)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def editable_version
|
|
133
|
+
@app.get_edit_app_store_version(platform: @platform)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def discover_videos(root)
|
|
137
|
+
extensions = %w[mp4 mov m4v]
|
|
138
|
+
locales = Dir.children(root).select { |subdir| File.directory?(File.join(root, subdir)) }
|
|
139
|
+
all_videos = {}
|
|
140
|
+
locales.each do |locale|
|
|
141
|
+
dir = File.join(root, locale)
|
|
142
|
+
video_paths = Dir.children(dir)
|
|
143
|
+
.select { |filename| extensions.include?(File.extname(filename).delete(".").downcase) }
|
|
144
|
+
.map { |filename| File.join(dir, filename) }
|
|
145
|
+
.sort
|
|
146
|
+
valid_previews = []
|
|
147
|
+
video_paths.each do |path|
|
|
148
|
+
# require filename to contain a known preview type token
|
|
149
|
+
preview_type = Spaceship::ConnectAPI::AppPreviewSet.preview_type_from_filename(File.basename(path))
|
|
150
|
+
unless preview_type
|
|
151
|
+
UI.important("[#{locale}] '#{File.basename(path)}' does not contain any known preview device type. Skipping.")
|
|
152
|
+
next
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# enforce size constraint (under 500MB)
|
|
156
|
+
size_mb = File.size(path) / (1024.0 * 1024.0)
|
|
157
|
+
if size_mb > 500
|
|
158
|
+
UI.important("[#{locale}] '#{File.basename(path)}' is #{size_mb.round(1)}MB (> 500MB). Skipping.")
|
|
159
|
+
next
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# enforce duration constraints [15s..30s]. warn if duration can't be determined
|
|
163
|
+
duration = FastlaneCore::VideoUtils.read_video_duration_seconds(path)
|
|
164
|
+
if duration
|
|
165
|
+
if duration < 15.0 || duration > 30.0
|
|
166
|
+
UI.important("[#{locale}] '#{File.basename(path)}' duration is #{duration.round(2)}s (allowed: 15–30s). Skipping.")
|
|
167
|
+
next
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
UI.important("[#{locale}] Could not determine duration for '#{File.basename(path)}'. Proceeding anyway.")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# validate resolution against accepted canonical sizes; warn if resolution can't be determined
|
|
174
|
+
res = FastlaneCore::VideoUtils.read_video_resolution(path)
|
|
175
|
+
if res
|
|
176
|
+
unless Spaceship::ConnectAPI::AppPreviewSet.validate_video_resolution(res[0], res[1], preview_type)
|
|
177
|
+
UI.important("[#{locale}] '#{File.basename(path)}' has invalid resolution #{res.join('x')}. Skipping.")
|
|
178
|
+
next
|
|
179
|
+
end
|
|
180
|
+
else
|
|
181
|
+
UI.important("[#{locale}] Could not determine resolution for '#{File.basename(path)}'. Proceeding anyway.")
|
|
182
|
+
end
|
|
183
|
+
valid_previews << { path: path, preview_type: preview_type }
|
|
184
|
+
end
|
|
185
|
+
all_videos[locale] = valid_previews unless valid_previews.empty?
|
|
186
|
+
end
|
|
187
|
+
all_videos
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def delete_existing_previews(localization, sets)
|
|
191
|
+
sets.each do |set|
|
|
192
|
+
next unless set.app_previews && set.app_previews.any?
|
|
193
|
+
UI.message("Deleting #{set.app_previews.size} existing previews from set #{set.preview_type} for locale #{localization.locale} due to overwrite...")
|
|
194
|
+
set.app_previews.each do |preview|
|
|
195
|
+
begin
|
|
196
|
+
preview.delete!
|
|
197
|
+
rescue => e
|
|
198
|
+
UI.error("Failed to delete preview '#{preview.file_name}': #{e.class} - #{e.message}")
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -56,7 +56,7 @@ module Fastlane
|
|
|
56
56
|
description: "The key ID"),
|
|
57
57
|
FastlaneCore::ConfigItem.new(key: :issuer_id,
|
|
58
58
|
env_name: "APP_STORE_CONNECT_API_KEY_ISSUER_ID",
|
|
59
|
-
description: "The issuer ID. It
|
|
59
|
+
description: "The issuer ID. It should be nil if the key is individual API key",
|
|
60
60
|
optional: true),
|
|
61
61
|
FastlaneCore::ConfigItem.new(key: :key_filepath,
|
|
62
62
|
env_name: "APP_STORE_CONNECT_API_KEY_KEY_FILEPATH",
|
|
@@ -137,6 +137,10 @@ module Fastlane
|
|
|
137
137
|
key_id: "D83848D23",
|
|
138
138
|
issuer_id: "227b0bbf-ada8-458c-9d62-3d8022b7d07f",
|
|
139
139
|
key_content: "-----BEGIN EC PRIVATE KEY-----\nfewfawefawfe\n-----END EC PRIVATE KEY-----"
|
|
140
|
+
)',
|
|
141
|
+
'app_store_connect_api_key(
|
|
142
|
+
key_id: "D83848D23", # no issuer_id if the key is individual
|
|
143
|
+
key_content: "-----BEGIN EC PRIVATE KEY-----\nfewfawefawfe\n-----END EC PRIVATE KEY-----"
|
|
140
144
|
)'
|
|
141
145
|
]
|
|
142
146
|
end
|