fastlane 2.168.0 → 2.173.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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +68 -68
  3. data/cert/lib/cert/options.rb +2 -2
  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/add_git_tag.rb +12 -3
  12. data/fastlane/lib/fastlane/actions/artifactory.rb +36 -3
  13. data/fastlane/lib/fastlane/actions/build_app.rb +3 -1
  14. data/fastlane/lib/fastlane/actions/create_pull_request.rb +16 -1
  15. data/fastlane/lib/fastlane/actions/create_xcframework.rb +118 -0
  16. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +1 -1
  17. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +1 -1
  18. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +4 -0
  19. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +5 -1
  20. data/fastlane/lib/fastlane/actions/download_app_privacy_details_from_app_store.rb +142 -0
  21. data/fastlane/lib/fastlane/actions/download_dsyms.rb +0 -1
  22. data/fastlane/lib/fastlane/actions/git_commit.rb +6 -2
  23. data/fastlane/lib/fastlane/actions/github_api.rb +14 -3
  24. data/fastlane/lib/fastlane/actions/nexus_upload.rb +1 -0
  25. data/fastlane/lib/fastlane/actions/onesignal.rb +13 -3
  26. data/fastlane/lib/fastlane/actions/pod_push.rb +9 -0
  27. data/fastlane/lib/fastlane/actions/push_to_git_remote.rb +9 -1
  28. data/fastlane/lib/fastlane/actions/register_device.rb +1 -1
  29. data/fastlane/lib/fastlane/actions/register_devices.rb +2 -1
  30. data/fastlane/lib/fastlane/actions/set_github_release.rb +21 -8
  31. data/fastlane/lib/fastlane/actions/slack.rb +4 -5
  32. data/fastlane/lib/fastlane/actions/spm.rb +2 -2
  33. data/fastlane/lib/fastlane/actions/swiftlint.rb +4 -4
  34. data/fastlane/lib/fastlane/actions/upload_app_privacy_details_to_app_store.rb +291 -0
  35. data/fastlane/lib/fastlane/actions/xcode_install.rb +8 -5
  36. data/fastlane/lib/fastlane/cli_tools_distributor.rb +3 -0
  37. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -1
  38. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +3 -0
  39. data/fastlane/lib/fastlane/version.rb +1 -1
  40. data/fastlane/swift/Deliverfile.swift +2 -2
  41. data/fastlane/swift/DeliverfileProtocol.swift +2 -2
  42. data/fastlane/swift/Fastlane.swift +267 -45
  43. data/fastlane/swift/Gymfile.swift +2 -2
  44. data/fastlane/swift/GymfileProtocol.swift +15 -3
  45. data/fastlane/swift/Matchfile.swift +2 -2
  46. data/fastlane/swift/MatchfileProtocol.swift +2 -2
  47. data/fastlane/swift/Precheckfile.swift +2 -2
  48. data/fastlane/swift/PrecheckfileProtocol.swift +6 -2
  49. data/fastlane/swift/Scanfile.swift +2 -2
  50. data/fastlane/swift/ScanfileProtocol.swift +18 -2
  51. data/fastlane/swift/Screengrabfile.swift +2 -2
  52. data/fastlane/swift/ScreengrabfileProtocol.swift +2 -2
  53. data/fastlane/swift/Snapshotfile.swift +2 -2
  54. data/fastlane/swift/SnapshotfileProtocol.swift +15 -3
  55. data/fastlane_core/lib/fastlane_core/helper.rb +2 -2
  56. data/fastlane_core/lib/fastlane_core/ipa_file_analyser.rb +41 -16
  57. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +3 -4
  58. data/fastlane_core/lib/fastlane_core/project.rb +19 -6
  59. data/frameit/lib/frameit/device_types.rb +7 -1
  60. data/gym/lib/gym/error_handler.rb +8 -0
  61. data/gym/lib/gym/generators/build_command_generator.rb +3 -0
  62. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +2 -2
  63. data/gym/lib/gym/options.rb +19 -3
  64. data/match/lib/match/encryption/openssl.rb +4 -2
  65. data/match/lib/match/runner.rb +1 -1
  66. data/match/lib/match/storage/git_storage.rb +14 -10
  67. data/precheck/lib/precheck/options.rb +6 -1
  68. data/precheck/lib/precheck/rule_processor.rb +1 -1
  69. data/precheck/lib/precheck/runner.rb +1 -1
  70. data/scan/lib/scan/options.rb +22 -1
  71. data/scan/lib/scan/runner.rb +6 -1
  72. data/scan/lib/scan/slack_poster.rb +4 -1
  73. data/scan/lib/scan/test_command_generator.rb +3 -0
  74. data/screengrab/lib/screengrab/runner.rb +2 -0
  75. data/sigh/lib/sigh/runner.rb +1 -1
  76. data/snapshot/lib/assets/SnapshotHelper.swift +6 -2
  77. data/snapshot/lib/snapshot/options.rb +17 -2
  78. data/snapshot/lib/snapshot/update.rb +1 -1
  79. data/spaceship/lib/spaceship/client.rb +28 -1
  80. data/spaceship/lib/spaceship/connect_api.rb +6 -0
  81. data/spaceship/lib/spaceship/connect_api/api_client.rb +1 -1
  82. data/spaceship/lib/spaceship/connect_api/models/app.rb +19 -4
  83. data/spaceship/lib/spaceship/connect_api/models/app_data_usage.rb +59 -0
  84. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_category.rb +65 -0
  85. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_data_protection.rb +27 -0
  86. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_grouping.rb +18 -0
  87. data/spaceship/lib/spaceship/connect_api/models/app_data_usage_purposes.rb +37 -0
  88. data/spaceship/lib/spaceship/connect_api/models/app_data_usages_publish_state.rb +36 -0
  89. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +1 -0
  90. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +2 -0
  91. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +8 -1
  92. data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +9 -0
  93. data/spaceship/lib/spaceship/connect_api/models/device.rb +30 -0
  94. data/spaceship/lib/spaceship/connect_api/response.rb +3 -1
  95. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +12 -0
  96. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +103 -0
  97. data/spaceship/lib/spaceship/errors.rb +19 -0
  98. data/spaceship/lib/spaceship/two_step_or_factor_client.rb +19 -6
  99. data/spaceship/lib/spaceship/upgrade_2fa_later_client.rb +91 -0
  100. metadata +44 -23
  101. data/fastlane/lib/fastlane/actions/.download_dsyms.rb.swp +0 -0
  102. data/spaceship/lib/spaceship/connect_api/models/.app.rb.swp +0 -0
  103. data/spaceship/lib/spaceship/connect_api/models/.app_screenshot.rb.swp +0 -0
  104. data/spaceship/lib/spaceship/connect_api/models/.build.rb.swp +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 163e53283c2657d2e37607fefd6985e72a01946a0448bd5f07c11f399462b81c
4
- data.tar.gz: 3ad91b60198920341ca9725b227439a340ab997883fc40ecc5964b449502912a
3
+ metadata.gz: 84a3a2b2d4fc8ec05b2b67244f2a41b643b7a3596b42eafcd76e409681d419a8
4
+ data.tar.gz: 4843a267b0adad749d4a5670decd3d875285b387bbbd0df01c286b8ee5d64f9a
5
5
  SHA512:
6
- metadata.gz: 036c78bc28cf70e5a92682e54cd1c4ce22a99e5b8a6fef788b78e898200fe829927dbaa31b120bcfc6abe1504e235854fc94ee3415c854e845d7c68a7e250309
7
- data.tar.gz: 4f63a769059f91d26cf332c77c1eadceba29d0702d1222a41c6ccddfd2bcba91a8c53cbf83b0a04144d15c655361c93bd89f5f7769473549c1a5c86c1def4877
6
+ metadata.gz: b11081f274dfda7fb57b9de1940b7aeef425938b2f9811063b49306e82ddd274ac1f2c12f12ddfc6a750216438387d383da4c8a2ca504d768e9dff80c2b52bf1
7
+ data.tar.gz: 13c125644f4d2b7e2a8678c327b65aaeb9569b36acef098b6a229d79808ca472b1887ad30a917e40452725cc20386fb073417d8201d43c674420562bad7818dd
data/README.md CHANGED
@@ -34,49 +34,49 @@ 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='kohki-miki'>
38
- <a href='https://github.com/giginet'>
39
- <img src='https://github.com/giginet.png?size=140'>
37
+ <td id='daniel-jankowski'>
38
+ <a href='https://github.com/mollyIV'>
39
+ <img src='https://github.com/mollyIV.png?size=140'>
40
40
  </a>
41
- <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
41
+ <h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
42
42
  </td>
43
- <td id='jérôme-lacoste'>
44
- <a href='https://github.com/lacostej'>
45
- <img src='https://github.com/lacostej.png?size=140'>
43
+ <td id='helmut-januschka'>
44
+ <a href='https://github.com/hjanuschka'>
45
+ <img src='https://github.com/hjanuschka.png?size=140'>
46
46
  </a>
47
- <h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
47
+ <h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
48
48
  </td>
49
- <td id='manu-wallner'>
50
- <a href='https://github.com/milch'>
51
- <img src='https://github.com/milch.png?size=140'>
49
+ <td id='jan-piotrowski'>
50
+ <a href='https://github.com/janpio'>
51
+ <img src='https://github.com/janpio.png?size=140'>
52
52
  </a>
53
- <h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
53
+ <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
54
54
  </td>
55
- <td id='jimmy-dee'>
56
- <a href='https://github.com/jdee'>
57
- <img src='https://github.com/jdee.png?size=140'>
55
+ <td id='luka-mirosevic'>
56
+ <a href='https://github.com/lmirosevic'>
57
+ <img src='https://github.com/lmirosevic.png?size=140'>
58
58
  </a>
59
- <h4 align='center'>Jimmy Dee</h4>
59
+ <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
60
60
  </td>
61
- <td id='jorge-revuelta-h'>
62
- <a href='https://github.com/minuscorp'>
63
- <img src='https://github.com/minuscorp.png?size=140'>
61
+ <td id='felix-krause'>
62
+ <a href='https://github.com/KrauseFx'>
63
+ <img src='https://github.com/KrauseFx.png?size=140'>
64
64
  </a>
65
- <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
65
+ <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
66
66
  </td>
67
67
  </tr>
68
68
  <tr>
69
- <td id='aaron-brager'>
70
- <a href='https://github.com/getaaron'>
71
- <img src='https://github.com/getaaron.png?size=140'>
69
+ <td id='jimmy-dee'>
70
+ <a href='https://github.com/jdee'>
71
+ <img src='https://github.com/jdee.png?size=140'>
72
72
  </a>
73
- <h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
73
+ <h4 align='center'>Jimmy Dee</h4>
74
74
  </td>
75
- <td id='daniel-jankowski'>
76
- <a href='https://github.com/mollyIV'>
77
- <img src='https://github.com/mollyIV.png?size=140'>
75
+ <td id='kohki-miki'>
76
+ <a href='https://github.com/giginet'>
77
+ <img src='https://github.com/giginet.png?size=140'>
78
78
  </a>
79
- <h4 align='center'><a href='https://twitter.com/mollyIV'>Daniel Jankowski</a></h4>
79
+ <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
80
80
  </td>
81
81
  <td id='joshua-liebowitz'>
82
82
  <a href='https://github.com/taquitos'>
@@ -84,11 +84,11 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
84
84
  </a>
85
85
  <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
86
86
  </td>
87
- <td id='luka-mirosevic'>
88
- <a href='https://github.com/lmirosevic'>
89
- <img src='https://github.com/lmirosevic.png?size=140'>
87
+ <td id='fumiya-nakamura'>
88
+ <a href='https://github.com/nafu'>
89
+ <img src='https://github.com/nafu.png?size=140'>
90
90
  </a>
91
- <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
91
+ <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
92
92
  </td>
93
93
  <td id='matthew-ellis'>
94
94
  <a href='https://github.com/matthewellis'>
@@ -98,17 +98,11 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
98
98
  </td>
99
99
  </tr>
100
100
  <tr>
101
- <td id='iulian-onofrei'>
102
- <a href='https://github.com/revolter'>
103
- <img src='https://github.com/revolter.png?size=140'>
104
- </a>
105
- <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
106
- </td>
107
- <td id='danielle-tomlinson'>
108
- <a href='https://github.com/endocrimes'>
109
- <img src='https://github.com/endocrimes.png?size=140'>
101
+ <td id='maksym-grebenets'>
102
+ <a href='https://github.com/mgrebenets'>
103
+ <img src='https://github.com/mgrebenets.png?size=140'>
110
104
  </a>
111
- <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
105
+ <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
112
106
  </td>
113
107
  <td id='andrew-mcburney'>
114
108
  <a href='https://github.com/armcburney'>
@@ -122,37 +116,31 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
122
116
  </a>
123
117
  <h4 align='center'><a href='https://twitter.com/ott_max'>Max Ott</a></h4>
124
118
  </td>
125
- <td id='jan-piotrowski'>
126
- <a href='https://github.com/janpio'>
127
- <img src='https://github.com/janpio.png?size=140'>
128
- </a>
129
- <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
130
- </td>
131
- </tr>
132
- <tr>
133
119
  <td id='olivier-halligon'>
134
120
  <a href='https://github.com/AliSoftware'>
135
121
  <img src='https://github.com/AliSoftware.png?size=140'>
136
122
  </a>
137
123
  <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
138
124
  </td>
139
- <td id='felix-krause'>
140
- <a href='https://github.com/KrauseFx'>
141
- <img src='https://github.com/KrauseFx.png?size=140'>
125
+ <td id='iulian-onofrei'>
126
+ <a href='https://github.com/revolter'>
127
+ <img src='https://github.com/revolter.png?size=140'>
142
128
  </a>
143
- <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
129
+ <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
144
130
  </td>
145
- <td id='helmut-januschka'>
146
- <a href='https://github.com/hjanuschka'>
147
- <img src='https://github.com/hjanuschka.png?size=140'>
131
+ </tr>
132
+ <tr>
133
+ <td id='aaron-brager'>
134
+ <a href='https://github.com/getaaron'>
135
+ <img src='https://github.com/getaaron.png?size=140'>
148
136
  </a>
149
- <h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
137
+ <h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
150
138
  </td>
151
- <td id='maksym-grebenets'>
152
- <a href='https://github.com/mgrebenets'>
153
- <img src='https://github.com/mgrebenets.png?size=140'>
139
+ <td id='manu-wallner'>
140
+ <a href='https://github.com/milch'>
141
+ <img src='https://github.com/milch.png?size=140'>
154
142
  </a>
155
- <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
143
+ <h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
156
144
  </td>
157
145
  <td id='josh-holtz'>
158
146
  <a href='https://github.com/joshdholtz'>
@@ -160,20 +148,32 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
160
148
  </a>
161
149
  <h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
162
150
  </td>
163
- </tr>
164
- <tr>
165
- <td id='fumiya-nakamura'>
166
- <a href='https://github.com/nafu'>
167
- <img src='https://github.com/nafu.png?size=140'>
151
+ <td id='danielle-tomlinson'>
152
+ <a href='https://github.com/endocrimes'>
153
+ <img src='https://github.com/endocrimes.png?size=140'>
168
154
  </a>
169
- <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
155
+ <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
156
+ </td>
157
+ <td id='jérôme-lacoste'>
158
+ <a href='https://github.com/lacostej'>
159
+ <img src='https://github.com/lacostej.png?size=140'>
160
+ </a>
161
+ <h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
170
162
  </td>
163
+ </tr>
164
+ <tr>
171
165
  <td id='stefan-natchev'>
172
166
  <a href='https://github.com/snatchev'>
173
167
  <img src='https://github.com/snatchev.png?size=140'>
174
168
  </a>
175
169
  <h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
176
170
  </td>
171
+ <td id='jorge-revuelta-h'>
172
+ <a href='https://github.com/minuscorp'>
173
+ <img src='https://github.com/minuscorp.png?size=140'>
174
+ </a>
175
+ <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</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_.
@@ -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