fastlane 2.232.2 → 2.233.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +102 -102
- data/credentials_manager/lib/credentials_manager/appfile_config.rb +4 -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 +24 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c0eca61fa0396713a78e6de08eaca27b99061aa3316be3f456fd34b5aea05368
|
|
4
|
+
data.tar.gz: 1d31711ddcd3b0c745587cc2eb1adbcadefe599f40442efe06c65f4b78c50e8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 44249e561611f1f9cd093155561bc034e6c257abc1b8a0d45f2a7b36f8a8daf857cb0a9ec2a4efb40c056d135f595dff8313c4df157beb17ff0e7f823e37d1d9
|
|
7
|
+
data.tar.gz: 16e33277c3dfa404406a1664e6c1cb240e2df037052e437a3f525c7943956606d67b45688af945119ec29a034ef9ba3a8bb95505217edff32831d644838f9f30
|
data/README.md
CHANGED
|
@@ -35,55 +35,23 @@ 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='manish-rathi'>
|
|
39
|
-
<a href='https://github.com/crazymanish'>
|
|
40
|
-
<img src='https://github.com/crazymanish.png' width='140px;'>
|
|
41
|
-
</a>
|
|
42
|
-
<h4 align='center'><a href='https://twitter.com/iammanishrathi'>Manish Rathi</a></h4>
|
|
43
|
-
</td>
|
|
44
|
-
<td id='connor-tumbleson'>
|
|
45
|
-
<a href='https://github.com/ibotpeaches'>
|
|
46
|
-
<img src='https://github.com/ibotpeaches.png' width='140px;'>
|
|
47
|
-
</a>
|
|
48
|
-
<h4 align='center'><a href='https://twitter.com/ibotpeaches'>Connor Tumbleson</a></h4>
|
|
49
|
-
</td>
|
|
50
|
-
<td id='satoshi-namai'>
|
|
51
|
-
<a href='https://github.com/ainame'>
|
|
52
|
-
<img src='https://github.com/ainame.png' width='140px;'>
|
|
53
|
-
</a>
|
|
54
|
-
<h4 align='center'><a href='https://twitter.com/ainame'>Satoshi Namai</a></h4>
|
|
55
|
-
</td>
|
|
56
38
|
<td id='jorge-revuelta-h'>
|
|
57
39
|
<a href='https://github.com/minuscorp'>
|
|
58
40
|
<img src='https://github.com/minuscorp.png' width='140px;'>
|
|
59
41
|
</a>
|
|
60
42
|
<h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
|
|
61
43
|
</td>
|
|
62
|
-
<td id='
|
|
63
|
-
<a href='https://github.com/
|
|
64
|
-
<img src='https://github.com/
|
|
65
|
-
</a>
|
|
66
|
-
<h4 align='center'><a href='https://twitter.com/rogerluan_'>Roger Oba</a></h4>
|
|
67
|
-
</td>
|
|
68
|
-
</tr>
|
|
69
|
-
<tr>
|
|
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
|
-
</a>
|
|
74
|
-
<h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
|
|
75
|
-
</td>
|
|
76
|
-
<td id='helmut-januschka'>
|
|
77
|
-
<a href='https://github.com/hjanuschka'>
|
|
78
|
-
<img src='https://github.com/hjanuschka.png' width='140px;'>
|
|
44
|
+
<td id='łukasz-grabowski'>
|
|
45
|
+
<a href='https://github.com/lucgrabowski'>
|
|
46
|
+
<img src='https://github.com/lucgrabowski.png' width='140px;'>
|
|
79
47
|
</a>
|
|
80
|
-
<h4 align='center'
|
|
48
|
+
<h4 align='center'>Łukasz Grabowski</h4>
|
|
81
49
|
</td>
|
|
82
|
-
<td id='
|
|
83
|
-
<a href='https://github.com/
|
|
84
|
-
<img src='https://github.com/
|
|
50
|
+
<td id='fumiya-nakamura'>
|
|
51
|
+
<a href='https://github.com/nafu'>
|
|
52
|
+
<img src='https://github.com/nafu.png' width='140px;'>
|
|
85
53
|
</a>
|
|
86
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
54
|
+
<h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
|
|
87
55
|
</td>
|
|
88
56
|
<td id='josh-holtz'>
|
|
89
57
|
<a href='https://github.com/joshdholtz'>
|
|
@@ -91,83 +59,69 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
91
59
|
</a>
|
|
92
60
|
<h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
|
|
93
61
|
</td>
|
|
94
|
-
<td id='
|
|
95
|
-
<a href='https://github.com/
|
|
96
|
-
<img src='https://github.com/
|
|
62
|
+
<td id='aaron-brager'>
|
|
63
|
+
<a href='https://github.com/getaaron'>
|
|
64
|
+
<img src='https://github.com/getaaron.png' width='140px;'>
|
|
97
65
|
</a>
|
|
98
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
66
|
+
<h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
|
|
99
67
|
</td>
|
|
100
68
|
</tr>
|
|
101
69
|
<tr>
|
|
70
|
+
<td id='jérôme-lacoste'>
|
|
71
|
+
<a href='https://github.com/lacostej'>
|
|
72
|
+
<img src='https://github.com/lacostej.png' width='140px;'>
|
|
73
|
+
</a>
|
|
74
|
+
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
|
75
|
+
</td>
|
|
102
76
|
<td id='andrew-mcburney'>
|
|
103
77
|
<a href='https://github.com/armcburney'>
|
|
104
78
|
<img src='https://github.com/armcburney.png' width='140px;'>
|
|
105
79
|
</a>
|
|
106
80
|
<h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
|
|
107
81
|
</td>
|
|
108
|
-
<td id='daniel-jankowski'>
|
|
109
|
-
<a href='https://github.com/mollyIV'>
|
|
110
|
-
<img src='https://github.com/mollyIV.png' width='140px;'>
|
|
111
|
-
</a>
|
|
112
|
-
<h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
|
|
113
|
-
</td>
|
|
114
|
-
<td id='łukasz-grabowski'>
|
|
115
|
-
<a href='https://github.com/lucgrabowski'>
|
|
116
|
-
<img src='https://github.com/lucgrabowski.png' width='140px;'>
|
|
117
|
-
</a>
|
|
118
|
-
<h4 align='center'>Łukasz Grabowski</h4>
|
|
119
|
-
</td>
|
|
120
|
-
<td id='jimmy-dee'>
|
|
121
|
-
<a href='https://github.com/jdee'>
|
|
122
|
-
<img src='https://github.com/jdee.png' width='140px;'>
|
|
123
|
-
</a>
|
|
124
|
-
<h4 align='center'>Jimmy Dee</h4>
|
|
125
|
-
</td>
|
|
126
82
|
<td id='kohki-miki'>
|
|
127
83
|
<a href='https://github.com/giginet'>
|
|
128
84
|
<img src='https://github.com/giginet.png' width='140px;'>
|
|
129
85
|
</a>
|
|
130
86
|
<h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
|
|
131
87
|
</td>
|
|
132
|
-
</tr>
|
|
133
|
-
<tr>
|
|
134
88
|
<td id='olivier-halligon'>
|
|
135
89
|
<a href='https://github.com/AliSoftware'>
|
|
136
90
|
<img src='https://github.com/AliSoftware.png' width='140px;'>
|
|
137
91
|
</a>
|
|
138
92
|
<h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
|
|
139
93
|
</td>
|
|
140
|
-
<td id='
|
|
141
|
-
<a href='https://github.com/
|
|
142
|
-
<img src='https://github.com/
|
|
94
|
+
<td id='connor-tumbleson'>
|
|
95
|
+
<a href='https://github.com/ibotpeaches'>
|
|
96
|
+
<img src='https://github.com/ibotpeaches.png' width='140px;'>
|
|
143
97
|
</a>
|
|
144
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
98
|
+
<h4 align='center'><a href='https://twitter.com/ibotpeaches'>Connor Tumbleson</a></h4>
|
|
145
99
|
</td>
|
|
146
|
-
|
|
147
|
-
<
|
|
148
|
-
<
|
|
100
|
+
</tr>
|
|
101
|
+
<tr>
|
|
102
|
+
<td id='satoshi-namai'>
|
|
103
|
+
<a href='https://github.com/ainame'>
|
|
104
|
+
<img src='https://github.com/ainame.png' width='140px;'>
|
|
149
105
|
</a>
|
|
150
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
106
|
+
<h4 align='center'><a href='https://twitter.com/ainame'>Satoshi Namai</a></h4>
|
|
151
107
|
</td>
|
|
152
|
-
<td id='
|
|
153
|
-
<a href='https://github.com/
|
|
154
|
-
<img src='https://github.com/
|
|
108
|
+
<td id='maksym-grebenets'>
|
|
109
|
+
<a href='https://github.com/mgrebenets'>
|
|
110
|
+
<img src='https://github.com/mgrebenets.png' width='140px;'>
|
|
155
111
|
</a>
|
|
156
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
112
|
+
<h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
|
|
157
113
|
</td>
|
|
158
|
-
<td id='
|
|
159
|
-
<a href='https://github.com/
|
|
160
|
-
<img src='https://github.com/
|
|
114
|
+
<td id='stefan-natchev'>
|
|
115
|
+
<a href='https://github.com/snatchev'>
|
|
116
|
+
<img src='https://github.com/snatchev.png' width='140px;'>
|
|
161
117
|
</a>
|
|
162
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
118
|
+
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
|
163
119
|
</td>
|
|
164
|
-
|
|
165
|
-
<
|
|
166
|
-
<
|
|
167
|
-
<a href='https://github.com/lacostej'>
|
|
168
|
-
<img src='https://github.com/lacostej.png' width='140px;'>
|
|
120
|
+
<td id='max-ott'>
|
|
121
|
+
<a href='https://github.com/max-ott'>
|
|
122
|
+
<img src='https://github.com/max-ott.png' width='140px;'>
|
|
169
123
|
</a>
|
|
170
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
124
|
+
<h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
|
|
171
125
|
</td>
|
|
172
126
|
<td id='iulian-onofrei'>
|
|
173
127
|
<a href='https://github.com/revolter'>
|
|
@@ -175,37 +129,83 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
175
129
|
</a>
|
|
176
130
|
<h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
|
|
177
131
|
</td>
|
|
132
|
+
</tr>
|
|
133
|
+
<tr>
|
|
134
|
+
<td id='matthew-ellis'>
|
|
135
|
+
<a href='https://github.com/matthewellis'>
|
|
136
|
+
<img src='https://github.com/matthewellis.png' width='140px;'>
|
|
137
|
+
</a>
|
|
138
|
+
<h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
|
|
139
|
+
</td>
|
|
140
|
+
<td id='manish-rathi'>
|
|
141
|
+
<a href='https://github.com/crazymanish'>
|
|
142
|
+
<img src='https://github.com/crazymanish.png' width='140px;'>
|
|
143
|
+
</a>
|
|
144
|
+
<h4 align='center'><a href='https://twitter.com/iammanishrathi'>Manish Rathi</a></h4>
|
|
145
|
+
</td>
|
|
146
|
+
<td id='danielle-tomlinson'>
|
|
147
|
+
<a href='https://github.com/endocrimes'>
|
|
148
|
+
<img src='https://github.com/endocrimes.png' width='140px;'>
|
|
149
|
+
</a>
|
|
150
|
+
<h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
|
|
151
|
+
</td>
|
|
152
|
+
<td id='joshua-liebowitz'>
|
|
153
|
+
<a href='https://github.com/taquitos'>
|
|
154
|
+
<img src='https://github.com/taquitos.png' width='140px;'>
|
|
155
|
+
</a>
|
|
156
|
+
<h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
|
|
157
|
+
</td>
|
|
158
|
+
<td id='jan-piotrowski'>
|
|
159
|
+
<a href='https://github.com/janpio'>
|
|
160
|
+
<img src='https://github.com/janpio.png' width='140px;'>
|
|
161
|
+
</a>
|
|
162
|
+
<h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
|
|
163
|
+
</td>
|
|
164
|
+
</tr>
|
|
165
|
+
<tr>
|
|
166
|
+
<td id='helmut-januschka'>
|
|
167
|
+
<a href='https://github.com/hjanuschka'>
|
|
168
|
+
<img src='https://github.com/hjanuschka.png' width='140px;'>
|
|
169
|
+
</a>
|
|
170
|
+
<h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
|
|
171
|
+
</td>
|
|
178
172
|
<td id='felix-krause'>
|
|
179
173
|
<a href='https://github.com/KrauseFx'>
|
|
180
174
|
<img src='https://github.com/KrauseFx.png' width='140px;'>
|
|
181
175
|
</a>
|
|
182
176
|
<h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
|
|
183
177
|
</td>
|
|
184
|
-
<td id='
|
|
185
|
-
<a href='https://github.com/
|
|
186
|
-
<img src='https://github.com/
|
|
178
|
+
<td id='jimmy-dee'>
|
|
179
|
+
<a href='https://github.com/jdee'>
|
|
180
|
+
<img src='https://github.com/jdee.png' width='140px;'>
|
|
187
181
|
</a>
|
|
188
|
-
<h4 align='center'
|
|
182
|
+
<h4 align='center'>Jimmy Dee</h4>
|
|
189
183
|
</td>
|
|
190
|
-
<td id='
|
|
191
|
-
<a href='https://github.com/
|
|
192
|
-
<img src='https://github.com/
|
|
184
|
+
<td id='daniel-jankowski'>
|
|
185
|
+
<a href='https://github.com/mollyIV'>
|
|
186
|
+
<img src='https://github.com/mollyIV.png' width='140px;'>
|
|
193
187
|
</a>
|
|
194
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
188
|
+
<h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
|
|
189
|
+
</td>
|
|
190
|
+
<td id='manu-wallner'>
|
|
191
|
+
<a href='https://github.com/milch'>
|
|
192
|
+
<img src='https://github.com/milch.png' width='140px;'>
|
|
193
|
+
</a>
|
|
194
|
+
<h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</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='luka-mirosevic'>
|
|
199
|
+
<a href='https://github.com/lmirosevic'>
|
|
200
|
+
<img src='https://github.com/lmirosevic.png' width='140px;'>
|
|
201
201
|
</a>
|
|
202
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
202
|
+
<h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
|
|
203
203
|
</td>
|
|
204
|
-
<td id='
|
|
205
|
-
<a href='https://github.com/
|
|
206
|
-
<img src='https://github.com/
|
|
204
|
+
<td id='roger-oba'>
|
|
205
|
+
<a href='https://github.com/rogerluan'>
|
|
206
|
+
<img src='https://github.com/rogerluan.png' width='140px;'>
|
|
207
207
|
</a>
|
|
208
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
208
|
+
<h4 align='center'><a href='https://twitter.com/rogerluan_'>Roger Oba</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)
|
|
@@ -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
|