fastlane 2.169.0 → 2.170.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 +78 -78
- data/deliver/lib/deliver/upload_metadata.rb +3 -3
- data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +1 -1
- data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +4 -0
- data/fastlane/lib/fastlane/actions/onesignal.rb +13 -3
- data/fastlane/lib/fastlane/actions/upload_app_privacy_details_to_app_store.rb +289 -0
- data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -1
- data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +3 -0
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +1 -1
- data/fastlane/swift/Fastlane.swift +44 -3
- 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/match/lib/match/runner.rb +1 -1
- data/spaceship/lib/spaceship/connect_api.rb +6 -0
- data/{sigh/lib/sigh/.options.rb.swp → spaceship/lib/spaceship/connect_api/models/.app_data_usage_data_protection.rb.swp} +0 -0
- data/spaceship/lib/spaceship/connect_api/models/app_data_usage.rb +59 -0
- data/spaceship/lib/spaceship/connect_api/models/app_data_usage_category.rb +65 -0
- data/spaceship/lib/spaceship/connect_api/models/app_data_usage_data_protection.rb +27 -0
- data/spaceship/lib/spaceship/connect_api/models/app_data_usage_grouping.rb +18 -0
- data/spaceship/lib/spaceship/connect_api/models/app_data_usage_purposes.rb +37 -0
- data/spaceship/lib/spaceship/connect_api/models/app_data_usages_publish_state.rb +36 -0
- data/spaceship/lib/spaceship/connect_api/models/device.rb +4 -0
- data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +103 -0
- metadata +26 -22
- data/cert/lib/cert/.options.rb.swp +0 -0
- data/cert/lib/cert/.runner.rb.swp +0 -0
- data/match/lib/match/.options.rb.swp +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77bbcab6a1ba00a457477ca27ddaca5f81485448e5022897e9c7ef43ff566991
|
|
4
|
+
data.tar.gz: 188796684ccb2fed018b9e4ada265a176aa074d09812592ce24d22c69fc48b7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 298f64e50908a6b7e40faa8e160d5e8b064054c2622bcaec9008f0aa861de6022187e0968a4398af07940d0f6383fbbfcb49cdc9136dab6cf6995f497f4d5440
|
|
7
|
+
data.tar.gz: 98361a556a41a534a68eddad93af5418be1bdcc8ae7b41b69308d109c37582896336ee5ff0e4ce791696320eacd7c21fc757125ef1bf8cfa7e945231ad279b87
|
data/README.md
CHANGED
|
@@ -34,49 +34,29 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
34
34
|
<!-- This table is regenerated and resorted on each release -->
|
|
35
35
|
<table id='team'>
|
|
36
36
|
<tr>
|
|
37
|
-
<td id='josh-holtz'>
|
|
38
|
-
<a href='https://github.com/joshdholtz'>
|
|
39
|
-
<img src='https://github.com/joshdholtz.png?size=140'>
|
|
40
|
-
</a>
|
|
41
|
-
<h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
|
|
42
|
-
</td>
|
|
43
|
-
<td id='maksym-grebenets'>
|
|
44
|
-
<a href='https://github.com/mgrebenets'>
|
|
45
|
-
<img src='https://github.com/mgrebenets.png?size=140'>
|
|
46
|
-
</a>
|
|
47
|
-
<h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
|
|
48
|
-
</td>
|
|
49
|
-
<td id='matthew-ellis'>
|
|
50
|
-
<a href='https://github.com/matthewellis'>
|
|
51
|
-
<img src='https://github.com/matthewellis.png?size=140'>
|
|
52
|
-
</a>
|
|
53
|
-
<h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
|
|
54
|
-
</td>
|
|
55
37
|
<td id='aaron-brager'>
|
|
56
38
|
<a href='https://github.com/getaaron'>
|
|
57
39
|
<img src='https://github.com/getaaron.png?size=140'>
|
|
58
40
|
</a>
|
|
59
41
|
<h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
|
|
60
42
|
</td>
|
|
61
|
-
<td id='
|
|
62
|
-
<a href='https://github.com/
|
|
63
|
-
<img src='https://github.com/
|
|
43
|
+
<td id='helmut-januschka'>
|
|
44
|
+
<a href='https://github.com/hjanuschka'>
|
|
45
|
+
<img src='https://github.com/hjanuschka.png?size=140'>
|
|
64
46
|
</a>
|
|
65
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
47
|
+
<h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
|
|
66
48
|
</td>
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
<a href='https://github.com/janpio'>
|
|
71
|
-
<img src='https://github.com/janpio.png?size=140'>
|
|
49
|
+
<td id='maksym-grebenets'>
|
|
50
|
+
<a href='https://github.com/mgrebenets'>
|
|
51
|
+
<img src='https://github.com/mgrebenets.png?size=140'>
|
|
72
52
|
</a>
|
|
73
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
53
|
+
<h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
|
|
74
54
|
</td>
|
|
75
|
-
<td id='
|
|
76
|
-
<a href='https://github.com/
|
|
77
|
-
<img src='https://github.com/
|
|
55
|
+
<td id='danielle-tomlinson'>
|
|
56
|
+
<a href='https://github.com/endocrimes'>
|
|
57
|
+
<img src='https://github.com/endocrimes.png?size=140'>
|
|
78
58
|
</a>
|
|
79
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
59
|
+
<h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
|
|
80
60
|
</td>
|
|
81
61
|
<td id='max-ott'>
|
|
82
62
|
<a href='https://github.com/max-ott'>
|
|
@@ -84,11 +64,25 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
84
64
|
</a>
|
|
85
65
|
<h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
|
|
86
66
|
</td>
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
<
|
|
67
|
+
</tr>
|
|
68
|
+
<tr>
|
|
69
|
+
<td id='andrew-mcburney'>
|
|
70
|
+
<a href='https://github.com/armcburney'>
|
|
71
|
+
<img src='https://github.com/armcburney.png?size=140'>
|
|
90
72
|
</a>
|
|
91
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
73
|
+
<h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
|
|
74
|
+
</td>
|
|
75
|
+
<td id='jérôme-lacoste'>
|
|
76
|
+
<a href='https://github.com/lacostej'>
|
|
77
|
+
<img src='https://github.com/lacostej.png?size=140'>
|
|
78
|
+
</a>
|
|
79
|
+
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
|
80
|
+
</td>
|
|
81
|
+
<td id='joshua-liebowitz'>
|
|
82
|
+
<a href='https://github.com/taquitos'>
|
|
83
|
+
<img src='https://github.com/taquitos.png?size=140'>
|
|
84
|
+
</a>
|
|
85
|
+
<h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
|
|
92
86
|
</td>
|
|
93
87
|
<td id='fumiya-nakamura'>
|
|
94
88
|
<a href='https://github.com/nafu'>
|
|
@@ -96,14 +90,46 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
96
90
|
</a>
|
|
97
91
|
<h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
|
|
98
92
|
</td>
|
|
93
|
+
<td id='matthew-ellis'>
|
|
94
|
+
<a href='https://github.com/matthewellis'>
|
|
95
|
+
<img src='https://github.com/matthewellis.png?size=140'>
|
|
96
|
+
</a>
|
|
97
|
+
<h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
|
|
98
|
+
</td>
|
|
99
99
|
</tr>
|
|
100
100
|
<tr>
|
|
101
|
+
<td id='stefan-natchev'>
|
|
102
|
+
<a href='https://github.com/snatchev'>
|
|
103
|
+
<img src='https://github.com/snatchev.png?size=140'>
|
|
104
|
+
</a>
|
|
105
|
+
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
|
106
|
+
</td>
|
|
107
|
+
<td id='luka-mirosevic'>
|
|
108
|
+
<a href='https://github.com/lmirosevic'>
|
|
109
|
+
<img src='https://github.com/lmirosevic.png?size=140'>
|
|
110
|
+
</a>
|
|
111
|
+
<h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
|
|
112
|
+
</td>
|
|
101
113
|
<td id='felix-krause'>
|
|
102
114
|
<a href='https://github.com/KrauseFx'>
|
|
103
115
|
<img src='https://github.com/KrauseFx.png?size=140'>
|
|
104
116
|
</a>
|
|
105
117
|
<h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
|
|
106
118
|
</td>
|
|
119
|
+
<td id='iulian-onofrei'>
|
|
120
|
+
<a href='https://github.com/revolter'>
|
|
121
|
+
<img src='https://github.com/revolter.png?size=140'>
|
|
122
|
+
</a>
|
|
123
|
+
<h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
|
|
124
|
+
</td>
|
|
125
|
+
<td id='olivier-halligon'>
|
|
126
|
+
<a href='https://github.com/AliSoftware'>
|
|
127
|
+
<img src='https://github.com/AliSoftware.png?size=140'>
|
|
128
|
+
</a>
|
|
129
|
+
<h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
|
|
130
|
+
</td>
|
|
131
|
+
</tr>
|
|
132
|
+
<tr>
|
|
107
133
|
<td id='daniel-jankowski'>
|
|
108
134
|
<a href='https://github.com/mollyIV'>
|
|
109
135
|
<img src='https://github.com/mollyIV.png?size=140'>
|
|
@@ -116,43 +142,17 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
116
142
|
</a>
|
|
117
143
|
<h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
|
|
118
144
|
</td>
|
|
119
|
-
<td id='
|
|
120
|
-
<a href='https://github.com/
|
|
121
|
-
<img src='https://github.com/
|
|
122
|
-
</a>
|
|
123
|
-
<h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
|
|
124
|
-
</td>
|
|
125
|
-
<td id='danielle-tomlinson'>
|
|
126
|
-
<a href='https://github.com/endocrimes'>
|
|
127
|
-
<img src='https://github.com/endocrimes.png?size=140'>
|
|
128
|
-
</a>
|
|
129
|
-
<h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
|
|
130
|
-
</td>
|
|
131
|
-
</tr>
|
|
132
|
-
<tr>
|
|
133
|
-
<td id='andrew-mcburney'>
|
|
134
|
-
<a href='https://github.com/armcburney'>
|
|
135
|
-
<img src='https://github.com/armcburney.png?size=140'>
|
|
136
|
-
</a>
|
|
137
|
-
<h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
|
|
138
|
-
</td>
|
|
139
|
-
<td id='jérôme-lacoste'>
|
|
140
|
-
<a href='https://github.com/lacostej'>
|
|
141
|
-
<img src='https://github.com/lacostej.png?size=140'>
|
|
142
|
-
</a>
|
|
143
|
-
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
|
144
|
-
</td>
|
|
145
|
-
<td id='stefan-natchev'>
|
|
146
|
-
<a href='https://github.com/snatchev'>
|
|
147
|
-
<img src='https://github.com/snatchev.png?size=140'>
|
|
145
|
+
<td id='jorge-revuelta-h'>
|
|
146
|
+
<a href='https://github.com/minuscorp'>
|
|
147
|
+
<img src='https://github.com/minuscorp.png?size=140'>
|
|
148
148
|
</a>
|
|
149
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
149
|
+
<h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
|
|
150
150
|
</td>
|
|
151
|
-
<td id='
|
|
152
|
-
<a href='https://github.com/
|
|
153
|
-
<img src='https://github.com/
|
|
151
|
+
<td id='kohki-miki'>
|
|
152
|
+
<a href='https://github.com/giginet'>
|
|
153
|
+
<img src='https://github.com/giginet.png?size=140'>
|
|
154
154
|
</a>
|
|
155
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
155
|
+
<h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
|
|
156
156
|
</td>
|
|
157
157
|
<td id='jimmy-dee'>
|
|
158
158
|
<a href='https://github.com/jdee'>
|
|
@@ -162,17 +162,17 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
162
162
|
</td>
|
|
163
163
|
</tr>
|
|
164
164
|
<tr>
|
|
165
|
-
<td id='
|
|
166
|
-
<a href='https://github.com/
|
|
167
|
-
<img src='https://github.com/
|
|
165
|
+
<td id='jan-piotrowski'>
|
|
166
|
+
<a href='https://github.com/janpio'>
|
|
167
|
+
<img src='https://github.com/janpio.png?size=140'>
|
|
168
168
|
</a>
|
|
169
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
169
|
+
<h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
|
|
170
170
|
</td>
|
|
171
|
-
<td id='
|
|
172
|
-
<a href='https://github.com/
|
|
173
|
-
<img src='https://github.com/
|
|
171
|
+
<td id='josh-holtz'>
|
|
172
|
+
<a href='https://github.com/joshdholtz'>
|
|
173
|
+
<img src='https://github.com/joshdholtz.png?size=140'>
|
|
174
174
|
</a>
|
|
175
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
175
|
+
<h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
|
|
176
176
|
</td>
|
|
177
177
|
</table>
|
|
178
178
|
|
|
@@ -375,7 +375,7 @@ module Deliver
|
|
|
375
375
|
# Check folder list (an empty folder signifies a language is required)
|
|
376
376
|
ignore_validation = options[:ignore_language_directory_validation]
|
|
377
377
|
Loader.language_folders(options[:metadata_path], ignore_validation).each do |lang_folder|
|
|
378
|
-
enabled_languages << lang_folder.
|
|
378
|
+
enabled_languages << lang_folder.basename unless enabled_languages.include?(lang_folder.basename)
|
|
379
379
|
end
|
|
380
380
|
|
|
381
381
|
return unless enabled_languages.include?("default")
|
|
@@ -414,7 +414,7 @@ module Deliver
|
|
|
414
414
|
# Check folder list (an empty folder signifies a language is required)
|
|
415
415
|
ignore_validation = options[:ignore_language_directory_validation]
|
|
416
416
|
Loader.language_folders(options[:metadata_path], ignore_validation).each do |lang_folder|
|
|
417
|
-
enabled_languages << lang_folder.
|
|
417
|
+
enabled_languages << lang_folder.basename unless enabled_languages.include?(lang_folder.basename)
|
|
418
418
|
end
|
|
419
419
|
|
|
420
420
|
# Mapping to strings because :default symbol can be passed in
|
|
@@ -531,7 +531,7 @@ module Deliver
|
|
|
531
531
|
|
|
532
532
|
UI.message("Loading '#{path}'...")
|
|
533
533
|
options[key] ||= {}
|
|
534
|
-
options[key][lang_folder.
|
|
534
|
+
options[key][lang_folder.basename] ||= File.read(path)
|
|
535
535
|
end
|
|
536
536
|
end
|
|
537
537
|
|
|
@@ -390,7 +390,7 @@ lane :beta do
|
|
|
390
390
|
end
|
|
391
391
|
```
|
|
392
392
|
|
|
393
|
-
By using the `force_for_new_devices` parameter, _match_ will check if the device count has changed since the last time you ran _match_, and automatically re-generate the provisioning profile if necessary. You can also use `force: true` to re-generate the provisioning profile on each run.
|
|
393
|
+
By using the `force_for_new_devices` parameter, _match_ will check if the (enabled) device count has changed since the last time you ran _match_, and automatically re-generate the provisioning profile if necessary. You can also use `force: true` to re-generate the provisioning profile on each run.
|
|
394
394
|
|
|
395
395
|
_**Important:** The `force_for_new_devices` parameter is ignored for App Store provisioning profiles since they don't contain any device information._
|
|
396
396
|
|
|
@@ -439,6 +439,10 @@ Use the `submission_information` parameter for additional submission specifiers,
|
|
|
439
439
|
fastlane deliver submit_build --build_number 830 --submission_information "{\"export_compliance_uses_encryption\": false, \"add_id_info_uses_idfa\": false }"
|
|
440
440
|
```
|
|
441
441
|
|
|
442
|
+
### App Privacy Details
|
|
443
|
+
|
|
444
|
+
Starting on December 8, 2020, Apple announced that developers are required to provide app privacy details that will help users understand an app's privacy practies. _deliver_ does not allow for updating of this information but this can be done with the _upload_app_privacy_details_to_app_store_ action. More information on [Uploading App Privacy Details](https://docs.fastlane.tools/uploading-app-privacy-details)
|
|
445
|
+
|
|
442
446
|
# Credentials
|
|
443
447
|
|
|
444
448
|
A detailed description about how your credentials are handled is available in a [credentials_manager](https://github.com/fastlane/fastlane/tree/master/credentials_manager).
|
|
@@ -17,6 +17,7 @@ module Fastlane
|
|
|
17
17
|
apns_p12_password = params[:apns_p12_password]
|
|
18
18
|
android_token = params[:android_token]
|
|
19
19
|
android_gcm_sender_id = params[:android_gcm_sender_id]
|
|
20
|
+
organization_id = params[:organization_id]
|
|
20
21
|
|
|
21
22
|
has_app_id = !app_id.empty?
|
|
22
23
|
has_app_name = !app_name.empty?
|
|
@@ -43,6 +44,7 @@ module Fastlane
|
|
|
43
44
|
|
|
44
45
|
payload["gcm_key"] = android_token unless android_token.nil?
|
|
45
46
|
payload["android_gcm_sender_id"] = android_gcm_sender_id unless android_gcm_sender_id.nil?
|
|
47
|
+
payload["organization_id"] = organization_id unless organization_id.nil?
|
|
46
48
|
|
|
47
49
|
# here's the actual lifting - POST or PUT to OneSignal
|
|
48
50
|
|
|
@@ -135,7 +137,13 @@ module Fastlane
|
|
|
135
137
|
env_name: "APNS_ENV",
|
|
136
138
|
description: "APNS environment",
|
|
137
139
|
optional: true,
|
|
138
|
-
default_value: 'production')
|
|
140
|
+
default_value: 'production'),
|
|
141
|
+
|
|
142
|
+
FastlaneCore::ConfigItem.new(key: :organization_id,
|
|
143
|
+
env_name: "ONE_SIGNAL_ORGANIZATION_ID",
|
|
144
|
+
sensitive: true,
|
|
145
|
+
description: "OneSignal Organization ID",
|
|
146
|
+
optional: true)
|
|
139
147
|
]
|
|
140
148
|
end
|
|
141
149
|
|
|
@@ -163,7 +171,8 @@ module Fastlane
|
|
|
163
171
|
android_gcm_sender_id: "Your Android GCM Sender ID (optional)",
|
|
164
172
|
apns_p12: "Path to Apple .p12 file (optional)",
|
|
165
173
|
apns_p12_password: "Password for .p12 file (optional)",
|
|
166
|
-
apns_env: "production/sandbox (defaults to production)"
|
|
174
|
+
apns_env: "production/sandbox (defaults to production)",
|
|
175
|
+
organization_id: "Onesignal organization id (optional)"
|
|
167
176
|
)',
|
|
168
177
|
'onesignal(
|
|
169
178
|
app_id: "Your OneSignal App ID",
|
|
@@ -173,7 +182,8 @@ module Fastlane
|
|
|
173
182
|
android_gcm_sender_id: "Your Android GCM Sender ID (optional)",
|
|
174
183
|
apns_p12: "Path to Apple .p12 file (optional)",
|
|
175
184
|
apns_p12_password: "Password for .p12 file (optional)",
|
|
176
|
-
apns_env: "production/sandbox (defaults to production)"
|
|
185
|
+
apns_env: "production/sandbox (defaults to production)",
|
|
186
|
+
organization_id: "Onesignal organization id (optional)"
|
|
177
187
|
)'
|
|
178
188
|
]
|
|
179
189
|
end
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
module Actions
|
|
3
|
+
class UploadAppPrivacyDetailsToAppStoreAction < Action
|
|
4
|
+
DEFAULT_PATH = Fastlane::Helper.fastlane_enabled_folder_path
|
|
5
|
+
DEFAULT_FILE_NAME = "app_privacy_details.json"
|
|
6
|
+
|
|
7
|
+
def self.run(params)
|
|
8
|
+
require 'spaceship'
|
|
9
|
+
|
|
10
|
+
# Prompts select team if multiple teams and none specified
|
|
11
|
+
UI.message("Login to App Store Connect (#{params[:username]})")
|
|
12
|
+
Spaceship::ConnectAPI.login(params[:username], use_portal: false, use_tunes: true, tunes_team_id: params[:team_id], team_name: params[:team_name])
|
|
13
|
+
UI.message("Login successful")
|
|
14
|
+
|
|
15
|
+
# Get App
|
|
16
|
+
app = Spaceship::ConnectAPI::App.find(params[:app_identifier])
|
|
17
|
+
unless app
|
|
18
|
+
UI.user_error!("Could not find app with bundle identifier '#{params[:app_identifier]}' on account #{params[:username]}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Attempt to load JSON file
|
|
22
|
+
usages_config = load_json_file(params)
|
|
23
|
+
|
|
24
|
+
# Start interactive questions to generate and save JSON file
|
|
25
|
+
unless usages_config
|
|
26
|
+
usages_config = ask_interactive_questions_for_json
|
|
27
|
+
|
|
28
|
+
if params[:skip_json_file_saving]
|
|
29
|
+
UI.message("Skipping JSON file saving...")
|
|
30
|
+
else
|
|
31
|
+
json = JSON.pretty_generate(usages_config)
|
|
32
|
+
path = output_path(params)
|
|
33
|
+
|
|
34
|
+
UI.message("Writing file to #{path}")
|
|
35
|
+
File.write(path, json)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Process JSON file to save app data usages to API
|
|
40
|
+
if params[:skip_upload]
|
|
41
|
+
UI.message("Skipping uploading of data... (so you can verify your JSON file)")
|
|
42
|
+
else
|
|
43
|
+
upload_app_data_usages(params, app, usages_config)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.load_json_file(params)
|
|
48
|
+
path = params[:json_path]
|
|
49
|
+
return nil if path.nil?
|
|
50
|
+
return JSON.parse(File.read(path))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.output_path(params)
|
|
54
|
+
path = params[:output_json_path]
|
|
55
|
+
return File.absolute_path(path)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.ask_interactive_questions_for_json(show_intro = true)
|
|
59
|
+
if show_intro
|
|
60
|
+
UI.important("You did not provide a JSON file for updating the app data usages")
|
|
61
|
+
UI.important("fastlane will now run you through interactive question to generate the JSON file")
|
|
62
|
+
UI.important("")
|
|
63
|
+
UI.important("This JSON file can be saved in source control and used in this action with the :json_file option")
|
|
64
|
+
|
|
65
|
+
unless UI.confirm("Ready to start?")
|
|
66
|
+
UI.user_error!("Cancelled")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Fetch categories and purposes used for generating interactive questions
|
|
71
|
+
categories = Spaceship::ConnectAPI::AppDataUsageCategory.all(includes: "grouping")
|
|
72
|
+
purposes = Spaceship::ConnectAPI::AppDataUsagePurpose.all
|
|
73
|
+
|
|
74
|
+
json = []
|
|
75
|
+
|
|
76
|
+
unless UI.confirm("Are you collecting data?")
|
|
77
|
+
json << {
|
|
78
|
+
"data_protections" => [Spaceship::ConnectAPI::AppDataUsageDataProtection::ID::DATA_NOT_COLLECTED]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return json
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
categories.each do |category|
|
|
85
|
+
# Ask if using category
|
|
86
|
+
next unless UI.confirm("Collect data for #{category.id}?")
|
|
87
|
+
|
|
88
|
+
purpose_names = purposes.map(&:id).join(', ')
|
|
89
|
+
UI.message("How will this data be used? You'll be offered with #{purpose_names}")
|
|
90
|
+
|
|
91
|
+
# Ask purposes
|
|
92
|
+
selected_purposes = []
|
|
93
|
+
loop do
|
|
94
|
+
purposes.each do |purpose|
|
|
95
|
+
selected_purposes << purpose if UI.confirm("Used for #{purpose.id}?")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
break unless selected_purposes.empty?
|
|
99
|
+
break unless UI.confirm("No purposes selected. Do you want to try again?")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Skip asking protections if purposes were skipped
|
|
103
|
+
next if selected_purposes.empty?
|
|
104
|
+
|
|
105
|
+
# Ask protections
|
|
106
|
+
is_linked_to_user = UI.confirm("Is #{category.id} linked to the user?")
|
|
107
|
+
is_used_for_tracking = UI.confirm("Is #{category.id} used for tracking purposes?")
|
|
108
|
+
|
|
109
|
+
# Map answers to values for API requests
|
|
110
|
+
protection_id = is_linked_to_user ? Spaceship::ConnectAPI::AppDataUsageDataProtection::ID::DATA_LINKED_TO_YOU : Spaceship::ConnectAPI::AppDataUsageDataProtection::ID::DATA_NOT_LINKED_TO_YOU
|
|
111
|
+
tracking_id = is_used_for_tracking ? Spaceship::ConnectAPI::AppDataUsageDataProtection::ID::DATA_USED_TO_TRACK_YOU : nil
|
|
112
|
+
|
|
113
|
+
json << {
|
|
114
|
+
"category" => category.id,
|
|
115
|
+
"purposes" => selected_purposes.map(&:id),
|
|
116
|
+
"data_protections" => [
|
|
117
|
+
protection_id, tracking_id
|
|
118
|
+
].compact
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Recursively call this method if no categories were selected for data collection
|
|
123
|
+
if json.empty?
|
|
124
|
+
UI.error("No categories were selected for data collection.")
|
|
125
|
+
json = ask_interactive_questions_for_json(false)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
return json
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.upload_app_data_usages(params, app, usages_config)
|
|
132
|
+
UI.message("Preparing to upload App Data Usage")
|
|
133
|
+
|
|
134
|
+
# Delete all existing usages for new ones
|
|
135
|
+
all_usages = Spaceship::ConnectAPI::AppDataUsage.all(app_id: app.id, includes: "category,grouping,purpose,dataProtection", limit: 500)
|
|
136
|
+
all_usages.each(&:delete!)
|
|
137
|
+
|
|
138
|
+
usages_config.each do |usage_config|
|
|
139
|
+
category = usage_config["category"]
|
|
140
|
+
purposes = usage_config["purposes"] || []
|
|
141
|
+
data_protections = usage_config["data_protections"] || []
|
|
142
|
+
|
|
143
|
+
# There will not be any purposes if "not collecting data"
|
|
144
|
+
# However, an AppDataUsage still needs to be created for not collecting data
|
|
145
|
+
# Creating an array with nil so that purposes can be iterated over and
|
|
146
|
+
# that AppDataUsage can be created
|
|
147
|
+
purposes = [nil] if purposes.empty?
|
|
148
|
+
|
|
149
|
+
purposes.each do |purpose|
|
|
150
|
+
data_protections.each do |data_protection|
|
|
151
|
+
if data_protection == Spaceship::ConnectAPI::AppDataUsageDataProtection::ID::DATA_NOT_COLLECTED
|
|
152
|
+
UI.message("Setting #{data_protection}")
|
|
153
|
+
else
|
|
154
|
+
UI.message("Setting #{category} and #{purpose} to #{data_protection}")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
Spaceship::ConnectAPI::AppDataUsage.create(
|
|
158
|
+
app_id: app.id,
|
|
159
|
+
app_data_usage_category_id: category,
|
|
160
|
+
app_data_usage_protection_id: data_protection,
|
|
161
|
+
app_data_usage_purpose_id: purpose
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Publish
|
|
168
|
+
if params[:skip_publish]
|
|
169
|
+
UI.message("Skipping app data usage publishing... (so you can verify on App Store Connect)")
|
|
170
|
+
else
|
|
171
|
+
publish_state = Spaceship::ConnectAPI::AppDataUsagesPublishState.get(app_id: app.id)
|
|
172
|
+
if publish_state.published
|
|
173
|
+
UI.important("App data usage is already published")
|
|
174
|
+
else
|
|
175
|
+
UI.important("App data usage not published! Going to publish...")
|
|
176
|
+
publish_state.publish!
|
|
177
|
+
UI.important("App data usage is now published")
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def self.description
|
|
183
|
+
"Upload App Privacy Details for an app in App Store Connect"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def self.available_options
|
|
187
|
+
user = CredentialsManager::AppfileConfig.try_fetch_value(:itunes_connect_id)
|
|
188
|
+
user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
|
|
189
|
+
|
|
190
|
+
[
|
|
191
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
|
192
|
+
env_name: "FASTLANE_USER",
|
|
193
|
+
description: "Your Apple ID Username for App Store Connect",
|
|
194
|
+
default_value: user,
|
|
195
|
+
default_value_dynamic: true),
|
|
196
|
+
FastlaneCore::ConfigItem.new(key: :app_identifier,
|
|
197
|
+
env_name: "UPLOAD_APP_PRIVACY_DETAILS_TO_APP_STORE_APP_IDENTIFIER",
|
|
198
|
+
description: "The bundle identifier of your app",
|
|
199
|
+
code_gen_sensitive: true,
|
|
200
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier),
|
|
201
|
+
default_value_dynamic: true),
|
|
202
|
+
FastlaneCore::ConfigItem.new(key: :team_id,
|
|
203
|
+
env_name: "FASTLANE_ITC_TEAM_ID",
|
|
204
|
+
description: "The ID of your App Store Connect team if you're in multiple teams",
|
|
205
|
+
optional: true,
|
|
206
|
+
is_string: false, # as we also allow integers, which we convert to strings anyway
|
|
207
|
+
code_gen_sensitive: true,
|
|
208
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:itc_team_id),
|
|
209
|
+
default_value_dynamic: true),
|
|
210
|
+
FastlaneCore::ConfigItem.new(key: :team_name,
|
|
211
|
+
env_name: "FASTLANE_ITC_TEAM_NAME",
|
|
212
|
+
description: "The name of your App Store Connect team if you're in multiple teams",
|
|
213
|
+
optional: true,
|
|
214
|
+
code_gen_sensitive: true,
|
|
215
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:itc_team_name),
|
|
216
|
+
default_value_dynamic: true),
|
|
217
|
+
|
|
218
|
+
# JSON paths
|
|
219
|
+
FastlaneCore::ConfigItem.new(key: :json_path,
|
|
220
|
+
env_name: "UPLOAD_APP_PRIVACY_DETAILS_TO_APP_STORE_JSON_PATH",
|
|
221
|
+
description: "Path to the app usage data JSON",
|
|
222
|
+
is_string: true,
|
|
223
|
+
optional: true,
|
|
224
|
+
verify_block: proc do |value|
|
|
225
|
+
UI.user_error!("Could not find JSON file at path '#{File.expand_path(value)}'") unless File.exist?(value)
|
|
226
|
+
UI.user_error!("'#{value}' doesn't seem to be a JSON file") unless FastlaneCore::Helper.json_file?(File.expand_path(value))
|
|
227
|
+
end),
|
|
228
|
+
FastlaneCore::ConfigItem.new(key: :output_json_path,
|
|
229
|
+
env_name: "UPLOAD_APP_PRIVACY_DETAILS_TO_APP_STORE_OUTPUT_JSON_PATH",
|
|
230
|
+
description: "Path to the app usage data JSON file generated by interactive questions",
|
|
231
|
+
conflicting_options: [:skip_json_file_saving],
|
|
232
|
+
default_value: File.join(DEFAULT_PATH, DEFAULT_FILE_NAME)),
|
|
233
|
+
|
|
234
|
+
# Skipping options
|
|
235
|
+
FastlaneCore::ConfigItem.new(key: :skip_json_file_saving,
|
|
236
|
+
env_name: "UPLOAD_APP_PRIVACY_DETAILS_TO_APP_STORE_OUTPUT_SKIP_JSON_FILE_SAVING",
|
|
237
|
+
description: "Whether to skip the saving of the JSON file",
|
|
238
|
+
conflicting_options: [:skip_output_json_path],
|
|
239
|
+
type: Boolean,
|
|
240
|
+
default_value: false),
|
|
241
|
+
FastlaneCore::ConfigItem.new(key: :skip_upload,
|
|
242
|
+
env_name: "UPLOAD_APP_PRIVACY_DETAILS_TO_APP_STORE_OUTPUT_SKIP_UPLOAD",
|
|
243
|
+
description: "Whether to skip the upload and only create the JSON file with interactive questions",
|
|
244
|
+
conflicting_options: [:skip_publish],
|
|
245
|
+
type: Boolean,
|
|
246
|
+
default_value: false),
|
|
247
|
+
FastlaneCore::ConfigItem.new(key: :skip_publish,
|
|
248
|
+
env_name: "UPLOAD_APP_PRIVACY_DETAILS_TO_APP_STORE_OUTPUT_SKIP_PUBLISH",
|
|
249
|
+
description: "Whether to skip the publishing",
|
|
250
|
+
conflicting_options: [:skip_upload],
|
|
251
|
+
type: Boolean,
|
|
252
|
+
default_value: false)
|
|
253
|
+
]
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def self.author
|
|
257
|
+
"joshdholtz"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def self.is_supported?(platform)
|
|
261
|
+
[:ios, :mac, :tvos].include?(platform)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def self.details
|
|
265
|
+
"Upload App Privacy Details for an app in App Store Connect. For more detail information, view https://docs.fastlane.tools/uploading-app-privacy-details"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def self.example_code
|
|
269
|
+
[
|
|
270
|
+
'upload_app_privacy_details_to_app_store(
|
|
271
|
+
username: "your@email.com",
|
|
272
|
+
team_name: "Your Team",
|
|
273
|
+
app_identifier: "com.your.bundle"
|
|
274
|
+
)',
|
|
275
|
+
'upload_app_privacy_details_to_app_store(
|
|
276
|
+
username: "your@email.com",
|
|
277
|
+
team_name: "Your Team",
|
|
278
|
+
app_identifier: "com.your.bundle",
|
|
279
|
+
json_path: "fastlane/app_data_usages.json"
|
|
280
|
+
)'
|
|
281
|
+
]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def self.category
|
|
285
|
+
:production
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|