fastlane 2.167.0 → 2.172.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +76 -76
  3. data/cert/lib/cert/options.rb +3 -3
  4. data/deliver/lib/deliver/app_screenshot.rb +5 -7
  5. data/deliver/lib/deliver/app_screenshot_validator.rb +108 -0
  6. data/deliver/lib/deliver/commands_generator.rb +1 -1
  7. data/deliver/lib/deliver/loader.rb +123 -21
  8. data/deliver/lib/deliver/setup.rb +8 -3
  9. data/deliver/lib/deliver/upload_metadata.rb +6 -10
  10. data/deliver/lib/deliver/upload_screenshots.rb +1 -64
  11. data/fastlane/lib/fastlane/actions/{.download_dsyms.rb.swp → .update_fastlane.rb.swp} +0 -0
  12. data/fastlane/lib/fastlane/actions/add_git_tag.rb +12 -3
  13. data/fastlane/lib/fastlane/actions/artifactory.rb +36 -3
  14. data/fastlane/lib/fastlane/actions/build_app.rb +3 -1
  15. data/fastlane/lib/fastlane/actions/create_pull_request.rb +16 -1
  16. data/fastlane/lib/fastlane/actions/create_xcframework.rb +118 -0
  17. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +1 -1
  18. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +1 -1
  19. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +4 -0
  20. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +5 -1
  21. data/fastlane/lib/fastlane/actions/download_app_privacy_details_from_app_store.rb +142 -0
  22. data/fastlane/lib/fastlane/actions/download_dsyms.rb +0 -1
  23. data/fastlane/lib/fastlane/actions/git_commit.rb +6 -2
  24. data/fastlane/lib/fastlane/actions/github_api.rb +14 -3
  25. data/fastlane/lib/fastlane/actions/nexus_upload.rb +1 -0
  26. data/fastlane/lib/fastlane/actions/onesignal.rb +13 -3
  27. data/fastlane/lib/fastlane/actions/pod_push.rb +9 -0
  28. data/fastlane/lib/fastlane/actions/push_to_git_remote.rb +9 -1
  29. data/fastlane/lib/fastlane/actions/register_device.rb +1 -1
  30. data/fastlane/lib/fastlane/actions/register_devices.rb +2 -1
  31. data/fastlane/lib/fastlane/actions/set_github_release.rb +21 -8
  32. data/fastlane/lib/fastlane/actions/slack.rb +4 -5
  33. data/fastlane/lib/fastlane/actions/slather.rb +2 -2
  34. data/fastlane/lib/fastlane/actions/spm.rb +2 -2
  35. data/fastlane/lib/fastlane/actions/swiftlint.rb +4 -4
  36. data/fastlane/lib/fastlane/actions/upload_app_privacy_details_to_app_store.rb +291 -0
  37. data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +3 -3
  38. data/fastlane/lib/fastlane/actions/xcode_install.rb +8 -5
  39. data/fastlane/lib/fastlane/cli_tools_distributor.rb +3 -0
  40. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -1
  41. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +3 -0
  42. data/fastlane/lib/fastlane/version.rb +1 -1
  43. data/fastlane/swift/Deliverfile.swift +2 -2
  44. data/fastlane/swift/DeliverfileProtocol.swift +2 -2
  45. data/fastlane/swift/Fastlane.swift +276 -54
  46. data/fastlane/swift/Gymfile.swift +2 -2
  47. data/fastlane/swift/GymfileProtocol.swift +15 -3
  48. data/fastlane/swift/Matchfile.swift +2 -2
  49. data/fastlane/swift/MatchfileProtocol.swift +4 -4
  50. data/fastlane/swift/Precheckfile.swift +2 -2
  51. data/fastlane/swift/PrecheckfileProtocol.swift +6 -2
  52. data/fastlane/swift/Scanfile.swift +2 -2
  53. data/fastlane/swift/ScanfileProtocol.swift +18 -2
  54. data/fastlane/swift/Screengrabfile.swift +2 -2
  55. data/fastlane/swift/ScreengrabfileProtocol.swift +2 -2
  56. data/fastlane/swift/Snapshotfile.swift +2 -2
  57. data/fastlane/swift/SnapshotfileProtocol.swift +15 -3
  58. data/fastlane_core/lib/fastlane_core/helper.rb +3 -3
  59. data/fastlane_core/lib/fastlane_core/ipa_file_analyser.rb +41 -16
  60. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +3 -4
  61. data/fastlane_core/lib/fastlane_core/project.rb +18 -5
  62. data/frameit/lib/frameit/device_types.rb +7 -1
  63. data/gym/lib/gym/generators/build_command_generator.rb +3 -0
  64. data/gym/lib/gym/options.rb +19 -3
  65. data/match/lib/match/encryption/openssl.rb +4 -2
  66. data/match/lib/match/module.rb +1 -1
  67. data/match/lib/match/options.rb +1 -1
  68. data/match/lib/match/runner.rb +1 -1
  69. data/match/lib/match/storage/git_storage.rb +14 -10
  70. data/precheck/lib/precheck/options.rb +6 -1
  71. data/precheck/lib/precheck/rule_processor.rb +1 -1
  72. data/precheck/lib/precheck/runner.rb +1 -1
  73. data/scan/lib/scan/options.rb +22 -1
  74. data/scan/lib/scan/runner.rb +7 -2
  75. data/scan/lib/scan/slack_poster.rb +4 -1
  76. data/scan/lib/scan/test_command_generator.rb +3 -0
  77. data/screengrab/lib/screengrab/runner.rb +2 -0
  78. data/sigh/lib/sigh/runner.rb +1 -1
  79. data/snapshot/lib/assets/SnapshotHelper.swift +6 -2
  80. data/snapshot/lib/snapshot/options.rb +17 -2
  81. data/snapshot/lib/snapshot/update.rb +1 -1
  82. data/spaceship/lib/spaceship/client.rb +14 -0
  83. data/spaceship/lib/spaceship/connect_api.rb +6 -0
  84. data/spaceship/lib/spaceship/connect_api/api_client.rb +1 -1
  85. data/spaceship/lib/spaceship/connect_api/models/age_rating_declaration.rb +3 -2
  86. data/spaceship/lib/spaceship/connect_api/models/app.rb +94 -54
  87. data/spaceship/lib/spaceship/connect_api/models/app_data_usage.rb +59 -0
  88. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_category.rb +65 -0
  89. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_data_protection.rb +27 -0
  90. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_grouping.rb +18 -0
  91. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_purposes.rb +37 -0
  92. data/spaceship/lib/spaceship/connect_api/models/app_data_usages_publish_state.rb +36 -0
  93. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +16 -10
  94. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +8 -4
  95. data/spaceship/lib/spaceship/connect_api/models/app_preview.rb +15 -11
  96. data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +13 -9
  97. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +9 -7
  98. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +15 -11
  99. data/spaceship/lib/spaceship/connect_api/models/app_store_review_attachment.rb +7 -5
  100. data/spaceship/lib/spaceship/connect_api/models/app_store_review_detail.rb +6 -4
  101. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +62 -37
  102. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +21 -14
  103. data/spaceship/lib/spaceship/connect_api/models/app_store_version_submission.rb +3 -2
  104. data/spaceship/lib/spaceship/connect_api/models/beta_app_review_submission.rb +3 -2
  105. data/spaceship/lib/spaceship/connect_api/models/beta_feedback.rb +6 -4
  106. data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +12 -2
  107. data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +12 -8
  108. data/spaceship/lib/spaceship/connect_api/models/build.rb +24 -16
  109. data/spaceship/lib/spaceship/connect_api/models/build_delivery.rb +3 -2
  110. data/spaceship/lib/spaceship/connect_api/models/bundle_id.rb +9 -6
  111. data/spaceship/lib/spaceship/connect_api/models/bundle_id_capability.rb +6 -4
  112. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +12 -8
  113. data/spaceship/lib/spaceship/connect_api/models/device.rb +36 -4
  114. data/spaceship/lib/spaceship/connect_api/models/idfa_declaration.rb +6 -4
  115. data/spaceship/lib/spaceship/connect_api/models/profile.rb +12 -8
  116. data/spaceship/lib/spaceship/connect_api/models/reset_ratings_request.rb +3 -2
  117. data/spaceship/lib/spaceship/connect_api/models/sandbox_tester.rb +9 -6
  118. data/spaceship/lib/spaceship/connect_api/models/territory.rb +3 -2
  119. data/spaceship/lib/spaceship/connect_api/models/user.rb +6 -4
  120. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +9 -6
  121. data/spaceship/lib/spaceship/connect_api/response.rb +3 -1
  122. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +12 -0
  123. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +103 -0
  124. data/spaceship/lib/spaceship/errors.rb +19 -0
  125. data/spaceship/lib/spaceship/tunes/iap_detail.rb +1 -1
  126. data/spaceship/lib/spaceship/tunes/tunes_client.rb +2 -2
  127. data/spaceship/lib/spaceship/two_step_or_factor_client.rb +19 -6
  128. data/supply/lib/supply/options.rb +1 -1
  129. data/supply/lib/supply/uploader.rb +3 -2
  130. metadata +45 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12fc2f2fba2826c89ba9d96fb23aaf99ff241513f980e97cc6bd911683b563d1
4
- data.tar.gz: 189a71d8c59ce592ec3ef3c94c6cd4605ae3e6e130fb8dfd57a286aeffbffad4
3
+ metadata.gz: d25e374341a7acedcadc6a1861a3f047f4e6f71312d68e6b770e083cfa527e7a
4
+ data.tar.gz: 01eac07e1ca532f1baf5bb23722927b87fc10d5aeb867db86d18d60eddc87f09
5
5
  SHA512:
6
- metadata.gz: 2ce1affc526767654afe6c01483ae298d0a99b09f433fdae244956ff01d46947fb785e3d934bfa052c16c3b1064659ff368eda6746181162964ffd139a46ff61
7
- data.tar.gz: 7b33693ca6d648c49065a72152357491945a43f2936b245dde1eda2d91855eb319d23a4080315f9eb1c70bf2f90d6e4df845611acfd8674b9853a83e0dcded15
6
+ metadata.gz: 2ccfbd1a683d17b27384030f99fb4e8db51feccdcca7bc959efb1b0455ded32efa4470b1082710388d6e6444e6c02e7c7ef1e7d8b8f0e6c88746ccb984246a70
7
+ data.tar.gz: 59e9908175276ce658cf959c647a3426d55688bfa0f181f4d1c8554d1a6a7f64f36926d55b730255c3156411259f0f8a25f5e166e8d0d8652f2f6059fba1d78a
data/README.md CHANGED
@@ -34,29 +34,61 @@ 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='iulian-onofrei'>
38
+ <a href='https://github.com/revolter'>
39
+ <img src='https://github.com/revolter.png?size=140'>
40
+ </a>
41
+ <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
42
+ </td>
43
+ <td id='stefan-natchev'>
44
+ <a href='https://github.com/snatchev'>
45
+ <img src='https://github.com/snatchev.png?size=140'>
46
+ </a>
47
+ <h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
48
+ </td>
37
49
  <td id='aaron-brager'>
38
50
  <a href='https://github.com/getaaron'>
39
51
  <img src='https://github.com/getaaron.png?size=140'>
40
52
  </a>
41
53
  <h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
42
54
  </td>
55
+ <td id='andrew-mcburney'>
56
+ <a href='https://github.com/armcburney'>
57
+ <img src='https://github.com/armcburney.png?size=140'>
58
+ </a>
59
+ <h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
60
+ </td>
61
+ <td id='fumiya-nakamura'>
62
+ <a href='https://github.com/nafu'>
63
+ <img src='https://github.com/nafu.png?size=140'>
64
+ </a>
65
+ <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <td id='olivier-halligon'>
70
+ <a href='https://github.com/AliSoftware'>
71
+ <img src='https://github.com/AliSoftware.png?size=140'>
72
+ </a>
73
+ <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
74
+ </td>
75
+ <td id='kohki-miki'>
76
+ <a href='https://github.com/giginet'>
77
+ <img src='https://github.com/giginet.png?size=140'>
78
+ </a>
79
+ <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
80
+ </td>
43
81
  <td id='jimmy-dee'>
44
82
  <a href='https://github.com/jdee'>
45
83
  <img src='https://github.com/jdee.png?size=140'>
46
84
  </a>
47
85
  <h4 align='center'>Jimmy Dee</h4>
48
86
  </td>
49
- <td id='max-ott'>
50
- <a href='https://github.com/max-ott'>
51
- <img src='https://github.com/max-ott.png?size=140'>
52
- </a>
53
- <h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
54
- </td>
55
- <td id='jorge-revuelta-h'>
56
- <a href='https://github.com/minuscorp'>
57
- <img src='https://github.com/minuscorp.png?size=140'>
87
+ <td id='felix-krause'>
88
+ <a href='https://github.com/KrauseFx'>
89
+ <img src='https://github.com/KrauseFx.png?size=140'>
58
90
  </a>
59
- <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
91
+ <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
60
92
  </td>
61
93
  <td id='manu-wallner'>
62
94
  <a href='https://github.com/milch'>
@@ -72,43 +104,49 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
72
104
  </a>
73
105
  <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
74
106
  </td>
75
- <td id='olivier-halligon'>
76
- <a href='https://github.com/AliSoftware'>
77
- <img src='https://github.com/AliSoftware.png?size=140'>
78
- </a>
79
- <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
80
- </td>
81
107
  <td id='jérôme-lacoste'>
82
108
  <a href='https://github.com/lacostej'>
83
109
  <img src='https://github.com/lacostej.png?size=140'>
84
110
  </a>
85
111
  <h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
86
112
  </td>
87
- <td id='luka-mirosevic'>
88
- <a href='https://github.com/lmirosevic'>
89
- <img src='https://github.com/lmirosevic.png?size=140'>
113
+ <td id='maksym-grebenets'>
114
+ <a href='https://github.com/mgrebenets'>
115
+ <img src='https://github.com/mgrebenets.png?size=140'>
90
116
  </a>
91
- <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
117
+ <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
92
118
  </td>
93
- <td id='stefan-natchev'>
94
- <a href='https://github.com/snatchev'>
95
- <img src='https://github.com/snatchev.png?size=140'>
119
+ <td id='max-ott'>
120
+ <a href='https://github.com/max-ott'>
121
+ <img src='https://github.com/max-ott.png?size=140'>
96
122
  </a>
97
- <h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
123
+ <h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
124
+ </td>
125
+ <td id='josh-holtz'>
126
+ <a href='https://github.com/joshdholtz'>
127
+ <img src='https://github.com/joshdholtz.png?size=140'>
128
+ </a>
129
+ <h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
98
130
  </td>
99
131
  </tr>
100
132
  <tr>
101
- <td id='joshua-liebowitz'>
102
- <a href='https://github.com/taquitos'>
103
- <img src='https://github.com/taquitos.png?size=140'>
133
+ <td id='luka-mirosevic'>
134
+ <a href='https://github.com/lmirosevic'>
135
+ <img src='https://github.com/lmirosevic.png?size=140'>
104
136
  </a>
105
- <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
137
+ <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
106
138
  </td>
107
- <td id='andrew-mcburney'>
108
- <a href='https://github.com/armcburney'>
109
- <img src='https://github.com/armcburney.png?size=140'>
139
+ <td id='danielle-tomlinson'>
140
+ <a href='https://github.com/endocrimes'>
141
+ <img src='https://github.com/endocrimes.png?size=140'>
110
142
  </a>
111
- <h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
143
+ <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
144
+ </td>
145
+ <td id='jorge-revuelta-h'>
146
+ <a href='https://github.com/minuscorp'>
147
+ <img src='https://github.com/minuscorp.png?size=140'>
148
+ </a>
149
+ <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
112
150
  </td>
113
151
  <td id='matthew-ellis'>
114
152
  <a href='https://github.com/matthewellis'>
@@ -116,64 +154,26 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
116
154
  </a>
117
155
  <h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
118
156
  </td>
119
- <td id='kohki-miki'>
120
- <a href='https://github.com/giginet'>
121
- <img src='https://github.com/giginet.png?size=140'>
157
+ <td id='joshua-liebowitz'>
158
+ <a href='https://github.com/taquitos'>
159
+ <img src='https://github.com/taquitos.png?size=140'>
122
160
  </a>
123
- <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
161
+ <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
124
162
  </td>
163
+ </tr>
164
+ <tr>
125
165
  <td id='daniel-jankowski'>
126
166
  <a href='https://github.com/mollyIV'>
127
167
  <img src='https://github.com/mollyIV.png?size=140'>
128
168
  </a>
129
169
  <h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
130
170
  </td>
131
- </tr>
132
- <tr>
133
171
  <td id='helmut-januschka'>
134
172
  <a href='https://github.com/hjanuschka'>
135
173
  <img src='https://github.com/hjanuschka.png?size=140'>
136
174
  </a>
137
175
  <h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
138
176
  </td>
139
- <td id='maksym-grebenets'>
140
- <a href='https://github.com/mgrebenets'>
141
- <img src='https://github.com/mgrebenets.png?size=140'>
142
- </a>
143
- <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
144
- </td>
145
- <td id='felix-krause'>
146
- <a href='https://github.com/KrauseFx'>
147
- <img src='https://github.com/KrauseFx.png?size=140'>
148
- </a>
149
- <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
150
- </td>
151
- <td id='iulian-onofrei'>
152
- <a href='https://github.com/revolter'>
153
- <img src='https://github.com/revolter.png?size=140'>
154
- </a>
155
- <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
156
- </td>
157
- <td id='josh-holtz'>
158
- <a href='https://github.com/joshdholtz'>
159
- <img src='https://github.com/joshdholtz.png?size=140'>
160
- </a>
161
- <h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
162
- </td>
163
- </tr>
164
- <tr>
165
- <td id='danielle-tomlinson'>
166
- <a href='https://github.com/endocrimes'>
167
- <img src='https://github.com/endocrimes.png?size=140'>
168
- </a>
169
- <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
170
- </td>
171
- <td id='fumiya-nakamura'>
172
- <a href='https://github.com/nafu'>
173
- <img src='https://github.com/nafu.png?size=140'>
174
- </a>
175
- <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
176
- </td>
177
177
  </table>
178
178
 
179
179
  Special thanks to all [contributors](https://github.com/fastlane/fastlane/graphs/contributors) for extending and improving _fastlane_.
@@ -109,7 +109,7 @@ module Cert
109
109
  short_option: "-p",
110
110
  env_name: "CERT_KEYCHAIN_PASSWORD",
111
111
  sensitive: true,
112
- description: "This might be required the first time you access certificates on a new mac. For the login/default keychain this is your account password",
112
+ description: "This might be required the first time you access certificates on a new mac. For the login/default keychain this is your macOS account password",
113
113
  optional: true),
114
114
  FastlaneCore::ConfigItem.new(key: :skip_set_partition_list,
115
115
  short_option: "-P",
@@ -119,11 +119,11 @@ module Cert
119
119
  default_value: false),
120
120
  FastlaneCore::ConfigItem.new(key: :platform,
121
121
  env_name: "CERT_PLATFORM",
122
- description: "Set the provisioning profile's platform (ios, macos)",
122
+ description: "Set the provisioning profile's platform (ios, macos, tvos)",
123
123
  default_value: "ios",
124
124
  verify_block: proc do |value|
125
125
  value = value.to_s
126
- pt = %w(macos ios)
126
+ pt = %w(macos ios tvos)
127
127
  UI.user_error!("Unsupported platform, must be: #{pt}") unless pt.include?(value)
128
128
  end)
129
129
  ]
@@ -82,15 +82,12 @@ module Deliver
82
82
  # @param path (String) path to the screenshot file
83
83
  # @param language (String) Language of this screenshot (e.g. English)
84
84
  # @param screen_size (Deliver::AppScreenshot::ScreenSize) the screen size, which
85
- # will automatically be calculated when you don't set it.
85
+ # will automatically be calculated when you don't set it. (Deprecated)
86
86
  def initialize(path, language, screen_size = nil)
87
+ UI.deprecated('`screen_size` for Deliver::AppScreenshot.new is deprecated in favor of the default behavior to calculate size automatically. Passed value is no longer validated.') if screen_size
87
88
  self.path = path
88
89
  self.language = language
89
- screen_size ||= self.class.calculate_screen_size(path)
90
-
91
- self.screen_size = screen_size
92
-
93
- UI.error("Looks like the screenshot given (#{path}) does not match the requirements of #{screen_size}") unless self.is_valid?
90
+ self.screen_size = screen_size || self.class.calculate_screen_size(path)
94
91
  end
95
92
 
96
93
  # The iTC API requires a different notation for the device
@@ -161,6 +158,7 @@ module Deliver
161
158
 
162
159
  # Validates the given screenshots (size and format)
163
160
  def is_valid?
161
+ UI.deprecated('Deliver::AppScreenshot#is_valid? is deprecated in favor of Deliver::AppScreenshotValidator')
164
162
  return false unless ["png", "PNG", "jpg", "JPG", "jpeg", "JPEG"].include?(self.path.split(".").last)
165
163
 
166
164
  return self.screen_size == self.class.calculate_screen_size(self.path)
@@ -350,7 +348,7 @@ module Deliver
350
348
  end
351
349
  end
352
350
 
353
- UI.user_error!("Unsupported screen size #{size} for path '#{path}'")
351
+ nil
354
352
  end
355
353
  end
356
354
 
@@ -0,0 +1,108 @@
1
+ require 'fastimage'
2
+
3
+ module Deliver
4
+ class AppScreenshotValidator
5
+ # A simple structure that holds error information as well as formatted error messages consistently
6
+ # Set `to_skip` to `true` when just needing to skip uploading rather than causing a crash.
7
+ class ValidationError
8
+ # Constants that can be given to `type` param
9
+ INVALID_SCREEN_SIZE = 'Invalid screen size'.freeze
10
+ UNACCEPTABLE_DEVICE = 'Not an accepted App Store Connect device'.freeze
11
+ INVALID_FILE_EXTENSION = 'Invalid file extension'.freeze
12
+ FILE_EXTENSION_MISMATCH = 'File extension mismatches its image format'.freeze
13
+
14
+ attr_reader :type, :path, :debug_info, :to_skip
15
+
16
+ def initialize(type: nil, path: nil, debug_info: nil, to_skip: false)
17
+ @type = type
18
+ @path = path
19
+ @debug_info = debug_info
20
+ @to_skip = to_skip
21
+ end
22
+
23
+ def to_s
24
+ "#{to_skip ? '🏃 Skipping' : '🚫 Error'}: #{path} - #{type} (#{debug_info})"
25
+ end
26
+
27
+ def inspect
28
+ "\"#{type}\""
29
+ end
30
+ end
31
+
32
+ # Access each array by symbol returned from FastImage.type
33
+ ALLOWED_SCREENSHOT_FILE_EXTENSION = { png: ['png', 'PNG'], jpeg: ['jpg', 'JPG', 'jpeg', 'JPEG'] }.freeze
34
+
35
+ APP_SCREENSHOT_SPEC_URL = 'https://help.apple.com/app-store-connect/#/devd274dd925'.freeze
36
+
37
+ # Validate a screenshot and inform an error message via `errors` parameter. `errors` is mutated
38
+ # to append the messages and each message should contain the corresponding path to let users know which file is throwing the error.
39
+ #
40
+ # @param screenshot [AppScreenshot]
41
+ # @param errors [Array<Deliver::AppScreenshotValidator::ValidationError>] Pass an array object to add validation errors when detecting errors.
42
+ # This will be mutated to add more error objects as validation detects errors.
43
+ # @return [Boolean] true if given screenshot is valid
44
+ def self.validate(screenshot, errors)
45
+ # Given screenshot will be diagnosed and errors found are accumulated
46
+ errors_found = []
47
+
48
+ validate_screen_size(screenshot, errors_found)
49
+ validate_device_type(screenshot, errors_found)
50
+ validate_file_extension_and_format(screenshot, errors_found)
51
+
52
+ # Merge errors found into given errors array
53
+ errors_found.each { |error| errors.push(error) }
54
+ errors_found.empty?
55
+ end
56
+
57
+ def self.validate_screen_size(screenshot, errors_found)
58
+ if screenshot.screen_size.nil?
59
+ errors_found << ValidationError.new(type: ValidationError::INVALID_SCREEN_SIZE,
60
+ path: screenshot.path,
61
+ debug_info: "Actual size is #{get_formatted_size(screenshot)}. See the specifications to fix #{APP_SCREENSHOT_SPEC_URL}")
62
+ end
63
+ end
64
+
65
+ # Checking if the device type exists in spaceship
66
+ # Ex: iPhone 6.1 inch isn't supported in App Store Connect but need
67
+ # to have it in there for frameit support
68
+ def self.validate_device_type(screenshot, errors_found)
69
+ if !screenshot.screen_size.nil? && screenshot.device_type.nil?
70
+ errors_found << ValidationError.new(type: ValidationError::UNACCEPTABLE_DEVICE,
71
+ path: screenshot.path,
72
+ debug_info: "Screen size #{screenshot.screen_size} is not accepted. See the specifications to fix #{APP_SCREENSHOT_SPEC_URL}",
73
+ to_skip: true)
74
+ end
75
+ end
76
+
77
+ def self.validate_file_extension_and_format(screenshot, errors_found)
78
+ extension = File.extname(screenshot.path).delete('.')
79
+ valid_file_extensions = ALLOWED_SCREENSHOT_FILE_EXTENSION.values.flatten
80
+ is_valid_extension = valid_file_extensions.include?(extension)
81
+
82
+ unless is_valid_extension
83
+ errors_found << ValidationError.new(type: ValidationError::INVALID_FILE_EXTENSION,
84
+ path: screenshot.path,
85
+ debug_info: "Only #{valid_file_extensions.join(', ')} are allowed")
86
+ end
87
+
88
+ format = FastImage.type(screenshot.path)
89
+ is_extension_matched = ALLOWED_SCREENSHOT_FILE_EXTENSION[format] &&
90
+ ALLOWED_SCREENSHOT_FILE_EXTENSION[format].include?(extension)
91
+
92
+ # This error only appears when file extension is valid
93
+ if is_valid_extension && !is_extension_matched
94
+ expected_extension = ALLOWED_SCREENSHOT_FILE_EXTENSION[format].first
95
+ expected_filename = File.basename(screenshot.path, File.extname(screenshot.path)) + ".#{expected_extension}"
96
+ errors_found << ValidationError.new(type: ValidationError::FILE_EXTENSION_MISMATCH,
97
+ path: screenshot.path,
98
+ debug_info: %(Actual format is "#{format}". Rename the filename to "#{expected_filename}".))
99
+ end
100
+ end
101
+
102
+ def self.get_formatted_size(screenshot)
103
+ size = FastImage.size(screenshot.path)
104
+ return size.join('x') if size
105
+ nil
106
+ end
107
+ end
108
+ end
@@ -177,7 +177,7 @@ module Deliver
177
177
  end
178
178
  end
179
179
 
180
- Deliver::Setup.new.generate_metadata_files(app, v, path)
180
+ Deliver::Setup.new.generate_metadata_files(app, v, path, options)
181
181
  end
182
182
  end
183
183
 
@@ -1,7 +1,6 @@
1
- require 'fastlane_core/languages'
2
- require 'spaceship/tunes/tunes'
3
-
4
1
  require_relative 'module'
2
+ require_relative 'app_screenshot'
3
+ require_relative 'app_screenshot_validator'
5
4
  require_relative 'upload_metadata'
6
5
  require_relative 'languages'
7
6
 
@@ -13,6 +12,7 @@ module Deliver
13
12
  IMESSAGE_DIR_NAME = "iMessage".freeze
14
13
  DEFAULT_DIR_NAME = "default".freeze
15
14
 
15
+ EXPANDABLE_DIR_NAMES = [APPLE_TV_DIR_NAME, IMESSAGE_DIR_NAME].freeze
16
16
  SPECIAL_DIR_NAMES = [APPLE_TV_DIR_NAME, IMESSAGE_DIR_NAME, DEFAULT_DIR_NAME].freeze
17
17
 
18
18
  # Some exception directories may exist from other actions that should not be iterated through
@@ -20,35 +20,137 @@ module Deliver
20
20
  FRAMEIT_FONTS_DIR_NAME = "fonts".freeze
21
21
  META_DIR_NAMES = UploadMetadata::ALL_META_SUB_DIRS.map(&:downcase)
22
22
 
23
- EXCEPTION_DIRECTORIES = (META_DIR_NAMES << SUPPLY_DIR_NAME << FRAMEIT_FONTS_DIR_NAME).freeze
23
+ EXCEPTION_DIRECTORIES = (META_DIR_NAMES << SUPPLY_DIR_NAME << FRAMEIT_FONTS_DIR_NAME).freeze
24
24
 
25
- def self.language_folders(root, ignore_validation)
26
- folders = Dir.glob(File.join(root, '*'))
25
+ # A class that represents language folder under screenshots or metadata folder
26
+ class LanguageFolder
27
+ attr_reader :path
28
+
29
+ # @return [String] A normalized language name that corresponds to the directory's name
30
+ attr_reader :language
31
+
32
+ def self.available_languages
33
+ # 2020-08-24 - Available locales are not available as an endpoint in App Store Connect
34
+ # Update with Spaceship::Tunes.client.available_languages.sort (as long as endpoint is avilable)
35
+ Deliver::Languages::ALL_LANGUAGES
36
+ end
37
+
38
+ def self.allowed_directory_names_with_case
39
+ available_languages + SPECIAL_DIR_NAMES
40
+ end
41
+
42
+ # @param path [String] A directory path otherwise this initializer fails
43
+ # @param nested [Boolan] Whether given path is nested of another special directory.
44
+ # This affects `expandable?` to return `false` when this set to `true`.
45
+ def initialize(path, nested: false)
46
+ raise(ArgumentError, "Given path must be a directory path - #{path}") unless File.directory?(path)
47
+ @path = path
48
+ @language = self.class.available_languages.find { |lang| basename.casecmp?(lang) }
49
+ @nested = nested
50
+ end
27
51
 
28
- # 2020-08-24 - Available locales are not available as an endpoint in App Store Connect
29
- # Update with Spaceship::Tunes.client.available_languages.sort (as long as endpoint is avilable)
30
- available_languages = Deliver::Languages::ALL_LANGUAGES
52
+ def nested?
53
+ @nested
54
+ end
31
55
 
32
- allowed_directory_names_with_case = (available_languages + SPECIAL_DIR_NAMES)
33
- allowed_directory_names = allowed_directory_names_with_case.map(&:downcase).freeze
56
+ def valid?
57
+ self.class.allowed_directory_names_with_case.any? { |name| name.casecmp?(basename) }
58
+ end
34
59
 
35
- selected_folders = folders.select do |path|
36
- File.directory?(path) && allowed_directory_names.include?(File.basename(path).downcase)
37
- end.sort
60
+ def expandable?
61
+ !nested? && EXPANDABLE_DIR_NAMES.any? { |name| name.casecmp?(basename) }
62
+ end
38
63
 
39
- # Gets list of folders that are not supported languages
40
- rejected_folders = folders.select do |path|
41
- normalized_path = File.basename(path).downcase
42
- File.directory?(path) && !allowed_directory_names.include?(normalized_path) && !EXCEPTION_DIRECTORIES.include?(normalized_path)
43
- end.sort
64
+ def skip?
65
+ EXCEPTION_DIRECTORIES.map(&:downcase).include?(basename.downcase)
66
+ end
67
+
68
+ def file_paths(extensions = '{png,jpg,jpeg}')
69
+ Dir.glob(File.join(path, "*.#{extensions}"), File::FNM_CASEFOLD).sort
70
+ end
71
+
72
+ def framed_file_paths(extensions = '{png,jpg,jpeg}')
73
+ Dir.glob(File.join(path, "*_framed.#{extensions}"), File::FNM_CASEFOLD).sort
74
+ end
75
+
76
+ def basename
77
+ File.basename(@path)
78
+ end
79
+ end
80
+
81
+ # Returns the list of valid app screenshot. When detecting invalid screenshots, this will cause an error.
82
+ #
83
+ # @param root [String] A directory path
84
+ # @param ignore_validation [String] Set false not to raise the error when finding invalid folder name
85
+ # @return [Array<AppScreenshot>] The list of AppScreenshot that exist under given `root` directory
86
+ def self.load_app_screenshots(root, ignore_validation)
87
+ screenshots = language_folders(root, ignore_validation, true).flat_map do |language_folder|
88
+ paths = if language_folder.framed_file_paths.count > 0
89
+ UI.important("Framed screenshots are detected! 🖼 Non-framed screenshot files may be skipped. 🏃")
90
+ # watchOS screenshots can be picked up even when framed ones were found since frameit doesn't support watchOS screenshots
91
+ framed_or_watch, skipped = language_folder.file_paths.partition { |path| path.downcase.include?('framed') || path.downcase.include?('watch') }
92
+ skipped.each { |path| UI.important("🏃 Skipping screenshot file: #{path}") }
93
+ framed_or_watch
94
+ else
95
+ language_folder.file_paths
96
+ end
97
+ paths.map { |path| AppScreenshot.new(path, language_folder.language) }
98
+ end
99
+
100
+ errors = []
101
+ valid_screenshots = screenshots.select { |screenshot| Deliver::AppScreenshotValidator.validate(screenshot, errors) }
102
+
103
+ errors_to_skip, errors_to_crash = errors.partition(&:to_skip)
104
+
105
+ unless errors_to_skip.empty?
106
+ UI.important("🏃 Screenshots to be skipped are detected!")
107
+ errors_to_skip.each { |error| UI.message(error) }
108
+ end
109
+
110
+ unless errors_to_crash.empty?
111
+ UI.important("🚫 Invalid screenshots were detected! Here are the reasons:")
112
+ errors_to_crash.each { |error| UI.error(error) }
113
+ UI.user_error!("Canceled uploading screenshots. Please check the error messages above and fix the screenshots.")
114
+ end
115
+
116
+ valid_screenshots
117
+ end
118
+
119
+ # Returns the list of language folders
120
+ #
121
+ # @param roort [String] A directory path to get the list of language folders
122
+ # @param ignore_validation [Boolean] Set false not to raise the error when finding invalid folder name
123
+ # @param expand_sub_folders [Boolean] Set true to expand special folders; such as "iMessage" to nested language folders
124
+ # @return [Array<LanguageFolder>] The list of LanguageFolder whose each of them
125
+ def self.language_folders(root, ignore_validation, expand_sub_folders = false)
126
+ folders = Dir.glob(File.join(root, '*'))
127
+ .select { |path| File.directory?(path) }
128
+ .map { |path| LanguageFolder.new(path, nested: false) }
129
+ .reject(&:skip?)
130
+
131
+ selected_folders, rejected_folders = folders.partition(&:valid?)
44
132
 
45
133
  if !ignore_validation && !rejected_folders.empty?
46
- rejected_folders = rejected_folders.map { |path| File.basename(path) }
134
+ rejected_folders = rejected_folders.map(&:basename)
47
135
  UI.user_error!("Unsupported directory name(s) for screenshots/metadata in '#{root}': #{rejected_folders.join(', ')}" \
48
- "\nValid directory names are: #{allowed_directory_names_with_case}" \
136
+ "\nValid directory names are: #{LanguageFolder.allowed_directory_names_with_case}" \
49
137
  "\n\nEnable 'ignore_language_directory_validation' to prevent this validation from happening")
50
138
  end
51
139
 
140
+ # Expand selected_folders for the special directories
141
+ if expand_sub_folders
142
+ selected_folders = selected_folders.flat_map do |folder|
143
+ if folder.expandable?
144
+ Dir.glob(File.join(folder.path, '*'))
145
+ .select { |p| File.directory?(p) }
146
+ .map { |p| LanguageFolder.new(p, nested: true) }
147
+ .select(&:valid?)
148
+ else
149
+ folder
150
+ end
151
+ end
152
+ end
153
+
52
154
  selected_folders
53
155
  end
54
156
  end