fastlane-plugin-universal_metadata 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 37aa001169b3af7ebd4de16a163a1582bce986c45a2c8cfb5aba140a1d2d66c0
4
+ data.tar.gz: 9f97e4cf3563d4dd1ca15b86c4ba22d7dc15f01589a5154d502c483906317b62
5
+ SHA512:
6
+ metadata.gz: 2d6d927ce29ccea66d60fc491b3bdc25b290a25289b6c16f635e7a78bec71807319fbfc5c1684590f0259d816ce3ce592f0913089ccaa4652e97799d588e04e6
7
+ data.tar.gz: 574d2ef31a422ed2cd99aea07b56e1fccd4e9ab5e1b42107896fa5c74bd0d1ce967351eec82fb6ee31dde770e3489c5764586efc1bd0a580a87bb00655465229
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 La Gregance <>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # Universal metadata
2
+
3
+ By default on Fastlane we use `upload_to_app_store` & `upload_to_play_store` to publish metadata on the store. But each of them use different folder structure to achieve the same goal.
4
+
5
+ The goal of this Fastlane plugin is to unify metadata & screenshots structure for both iOS and Android, and finally produce metadata folder that will be used by `upload_to_app_store` & `upload_to_play_store`.
6
+ This is ideal for React Native or Flutter project.
7
+
8
+ # Features
9
+
10
+ - Generate every screenshot size derived from a single file
11
+ - Generate every metadata for both Android & iOS from a single json file
12
+
13
+ # Setup
14
+
15
+ 1. First install the plugin by using `fastlane add_plugin universal_metadata`
16
+ 2. Run the command `fastlane run universal_metdata init:true languages:"en-US,fr-FR"`, this will init the folder `universal-metadata` with this structure
17
+
18
+ ```
19
+ universal-metadata
20
+ ├── screenshots
21
+ │ └── default
22
+ │ └── en-US
23
+ │ └── fr-FR
24
+ ├── description
25
+ │ └── default
26
+ │ ├── full_description.txt
27
+ │ └── short_description.txt
28
+ │ └── en-US
29
+ │ └── ...
30
+ │ └── fr-FR
31
+ │ └── ...
32
+ ├── release-notes
33
+ │ ├── default.txt
34
+ │ ├── en-US.txt
35
+ │ └── fr-FR.txt
36
+ └── metadata.json
37
+ ```
38
+
39
+
40
+ # Configuration
41
+
42
+ ## Metadata.json
43
+
44
+ Concerning metadata, most of things are configurable in the file `metadata.json`.
45
+
46
+ Sometime you will see that value are in the form `{ "default": "A value" }` in this case it means it’s a localized value and you can add translation for specific languages:
47
+
48
+ ```json
49
+ {
50
+ "default": "Hello world !",
51
+ "fr-FR": "Bonjour le monde !"
52
+ }
53
+ ```
54
+
55
+ If a value had a direct value, it means it cannot be translate.
56
+
57
+ ## Others metadata
58
+
59
+ We decide to put descriptions & release-notes under specific files cause theses metadata are often multilines and so not very convenient to define in a JSON file.
60
+
61
+ You can define you description following this structure:
62
+
63
+ ```
64
+ description
65
+ ├── default
66
+ │ ├── full_description.txt
67
+ │ └── short_description.txt <-- Android only
68
+ ├── en-US
69
+ │ ├── full_description.txt
70
+ │ └── short_description.txt
71
+ └── fr-FR <-- Remove language folder to use default instead
72
+ ├── full_description.txt
73
+ └── short_description.txt
74
+ ```
75
+
76
+ For release-notes it’s pretty close:
77
+
78
+ ```
79
+ release-notes
80
+ ├── default.txt
81
+ ├── en-US.txt
82
+ └── fr-FR.txt <-- Remove language file to use default instead
83
+ ```
84
+
85
+ ## Screenshots
86
+
87
+ Screenshots folder has a similar structure:
88
+
89
+ ```
90
+ screenshots
91
+ ├── default
92
+ │ ├── 1-screenshot.png
93
+ │ └── 2-screenshot.png
94
+ ├── en-US
95
+ └── fr-FR <-- Remove language folder to use default instead
96
+ ```
97
+
98
+ During the action, your screenshots will be resized to match size required by iOS & Android and put it the good folder.
99
+
100
+ ⚠️ Default folder is used only if the folder for language doesn’t exist (if language folder is empty, no screenshot will be uploaded).
101
+
102
+ You can configure screenshot behavior by adding tags in the name of the file :
103
+
104
+ | Tag | Description |
105
+ | --- | --- |
106
+ | tablet | Use the screenshot for tablet (without this tag it’s used only for phone) |
107
+ | contain | By default screenshot are resized with cover resizeMode. When the tag contain is present, screenshot will be resized with contain resizeMode (note for iOS, using contain tag can result to white border to match the exact size required by the App Store) |
108
+ | apple | If this tag is present, use the screenshot only for iOS |
109
+ | android | If this tag is present, use the screenshot only for Android |
110
+
111
+ So for example you can add a screenshot `random-name-apple-contain.png` that will be use only for iOS and use resizeMode contain.
112
+
113
+ ℹ️ The order of the screenshots on the store depends of alphabetical order of the screenshots names.
114
+
115
+ ## Result
116
+
117
+ By running `fastlane run universal_metdata languages:"en-US,fr-FR"` you will get the following result:
118
+
119
+ ```
120
+ screenshots
121
+ │ └── fr
122
+ │ ├── screen-1.png
123
+ │ └── screen-2.png
124
+ metadata
125
+ ├── android
126
+ │ └── fr
127
+ │ ├── images
128
+ │ │ ├── phoneScreenshots
129
+ │ │ │ ├── screen-1.png
130
+ │ │ │ └── screen-2.png
131
+ │ │ ├── sevenInchScreenshots
132
+ │ │ │ ├── screen-1.png
133
+ │ │ │ └── screen-2.png
134
+ │ │ └── tenInchScreenshots
135
+ │ │ │ ├── screen-1.png
136
+ │ │ │ └── screen-2.png
137
+ │ ├── full_description.txt
138
+ │ ├── short_description.txt
139
+ │ ├── title.txt
140
+ │ └── video.txt
141
+ ├── fr
142
+ │ ├── name.txt
143
+ │ ├── subtitle.txt
144
+ │ ├── privacy_url.txt
145
+ │ ├── description.txt
146
+ │ ├── keywords.txt
147
+ │ ├── release_notes.txt
148
+ │ ├── support_url.txt
149
+ │ ├── marketing_url.txt
150
+ │ └── promotional_text.txt
151
+ ├── copyright.txt
152
+ ├── primary_category.txt
153
+ ├── secondary_category.txt
154
+ ├── primary_first_sub_category.txt
155
+ ├── primary_second_sub_category.txt
156
+ ├── secondary_first_sub_category.txt
157
+ ├── secondary_second_sub_category.txt
158
+ ├── first_name.txt
159
+ ├── last_name.txt
160
+ ├── phone_number.txt
161
+ ├── email_address.txt
162
+ ├── demo_user.txt
163
+ ├── demo_password.txt
164
+ └── notes.txt
165
+ ```
166
+
167
+ Now you are able to run `upload_to_app_store` & `upload_to_play_store` without pain.
168
+
169
+ ℹ️ Don’t forget that you can use universal_metadata action in your lane, before uploading to store for example 😉
170
+
171
+ ## Params
172
+
173
+ You can use different params with the action:
174
+
175
+ | Param | ENV Variable | Description | Default |
176
+ | --- | --- | --- | --- |
177
+ | languages | LANGUAGES | List of languages to use (comma-separated) | |
178
+ | ios_screenshots_dir | IOS_SCREENSHOTS_DIR | The target dir for ios screenshots | fastlane/screenshots |
179
+ | skip_screenshots_resize | SKIP_SCREENSHOTS_RESIZE | Don’t resize screenshot if true | false |
180
+ | skip_screenshots | SKIP_SCREENSHOTS | Don’t manage screenshot at all if true | false |
181
+
182
+ ## Tips & Warning
183
+
184
+ ⚠️ Your metadata folder will be erased each time you run the action, be sure to not having sensitive data on it.
185
+ Also since it will be auto-generated, we recommend to add it to your `.gitignore`
186
+
187
+ ℹ️ Personally I like to have all the result under fastlane/metadata, that’s why i use the `ios_screenshots_dir`
188
+ params with the value `fastlane/metadata/screenshots`.
189
+
190
+
191
+ Because it’s not the default place for it, I have to add this to my Deliverfile:
192
+ ```
193
+ # Cause we use non-standard path for screenshots, we have to add this options
194
+ ignore_language_directory_validation(true)
195
+ screenshots_path("./fastlane/metadata/screenshots")
196
+
197
+ # Prevent same screenshot from being uploaded multiple times
198
+ overwrite_screenshots(true)
199
+ ```
@@ -0,0 +1,90 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/universal_metadata_metadata'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class UniversalMetadataAction < Action
7
+ def self.run(params)
8
+ languages = params[:languages].split(',')
9
+
10
+ if params[:init] then
11
+ Helper::UniversalMetadataInit.init(languages)
12
+ return
13
+ end
14
+
15
+ universal_metadata_folder = Helper::UniversalMetadataConst.universal_metadata_folder
16
+ metadata_folder = Helper::UniversalMetadataConst.metadata_folder
17
+
18
+ #####################################################
19
+ ############## GENERATE METADATA FILES ##############
20
+ #####################################################
21
+ metadata = JSON.parse(File.read(File.join(universal_metadata_folder, 'metadata.json')))
22
+ Helper::UniversalMetadataMetadata.generateMetadata(metadata, languages, metadata_folder)
23
+
24
+
25
+ ######################################################
26
+ ######### GENERATE SCREENSHOTS FOR EACH SIZE #########
27
+ ######################################################
28
+ Helper::UniversalMetadataScreenshots.generateScreenshots(languages, params, metadata_folder)
29
+ end
30
+
31
+ def self.description
32
+ "Unify metadata files for iOS & Android"
33
+ end
34
+
35
+ def self.authors
36
+ ["La Gregance"]
37
+ end
38
+
39
+ def self.return_value
40
+ # If your method provides a return value, you can describe here what it does
41
+ end
42
+
43
+ def self.details
44
+ # Optional:
45
+ "Unify metadata files for iOS & Android"
46
+ end
47
+
48
+ def self.available_options
49
+ [
50
+ FastlaneCore::ConfigItem.new(key: :init,
51
+ description: "Init universal-metadata folder",
52
+ optional: true,
53
+ default_value: false,
54
+ type: Boolean),
55
+ FastlaneCore::ConfigItem.new(key: :languages,
56
+ env_name: "LANGUAGES",
57
+ description: "Languages to which generate metadata folder (comma separated)",
58
+ optional: false,
59
+ type: String),
60
+ FastlaneCore::ConfigItem.new(key: :ios_screenshots_dir,
61
+ env_name: "IOS_SCREENSHOTS_DIR",
62
+ description: "Folder that will store the screenshots for iOS",
63
+ optional: true,
64
+ default_value: "fastlane/screenshots",
65
+ type: String),
66
+ FastlaneCore::ConfigItem.new(key: :skip_screenshots_resize,
67
+ env_name: "SKIP_SCREENSHOTS_RESIZE",
68
+ description: "If true, we don't resize screenshots before moving them",
69
+ optional: true,
70
+ default_value: false,
71
+ type: Boolean),
72
+ FastlaneCore::ConfigItem.new(key: :skip_screenshots,
73
+ env_name: "SKIP_SCREENSHOTS",
74
+ description: "If true, skip copying screenshot",
75
+ optional: true,
76
+ default_value: false,
77
+ type: Boolean)
78
+ ]
79
+ end
80
+
81
+ def self.is_supported?(platform)
82
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
83
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
84
+ #
85
+ # [:ios, :mac, :android].include?(platform)
86
+ true
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ module Helper
5
+ class UniversalMetadataConst
6
+ def self.universal_metadata_folder
7
+ 'fastlane/universal-metadata'
8
+ end
9
+
10
+ def self.metadata_folder
11
+ 'fastlane/metadata'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,86 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class UniversalMetadataInit
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::UniversalMetadataInit.your_method`
10
+ #
11
+ def self.init(languages)
12
+ folder = Helper::UniversalMetadataConst.universal_metadata_folder
13
+ if File.exist?(folder) then
14
+ UI.error("Folder universal-metadata already exists")
15
+ return
16
+ end
17
+
18
+ UI.message("Init universal-metadata folder")
19
+
20
+ # 1. Create file tree
21
+ FileUtils.mkdir_p(File.join(folder, 'screenshots/default'))
22
+
23
+ FileUtils.mkdir_p(File.join(folder, 'release-notes'))
24
+ File.write(File.join(folder, 'release-notes', 'default.txt'), "Default release note")
25
+
26
+ FileUtils.mkdir_p(File.join(folder, 'description/default'))
27
+ File.write(File.join(folder, 'description/default', 'full_description.txt'), "Default full description")
28
+ File.write(File.join(folder, 'description/default', 'short_description.txt'), "Default short description")
29
+
30
+ for lang in languages
31
+ FileUtils.mkdir_p(File.join(folder, 'screenshots', lang))
32
+
33
+ FileUtils.mkdir_p(File.join(folder, 'description', lang))
34
+ File.write(File.join(folder, 'description', lang, 'full_description.txt'), lang + " full description")
35
+ File.write(File.join(folder, 'description', lang, 'short_description.txt'), lang + " short description")
36
+
37
+ File.write(File.join(folder, 'release-notes', lang+'.txt'), lang + " release note")
38
+ end
39
+
40
+ # 2. Create metadata.json
41
+ metadata = {
42
+ "name": {
43
+ "default": "AppName"
44
+ },
45
+ "subtitle": {
46
+ "default": ""
47
+ },
48
+ "privacy_url": {
49
+ "default": ""
50
+ },
51
+ "copyright": "2023 La Gregance",
52
+ "apple": {
53
+ "primary_category": "",
54
+ "secondary_category": "",
55
+ "primary_first_sub_category": "",
56
+ "primary_second_sub_category": "",
57
+ "secondary_first_sub_category": "",
58
+ "secondary_second_sub_category": "",
59
+ "keywords": {
60
+ "default": ""
61
+ },
62
+ "support_url": {
63
+ "default": ""
64
+ },
65
+ "marketing_url": {
66
+ "default": ""
67
+ },
68
+ "promotional_text": {
69
+ "default": ""
70
+ },
71
+ "reviewInfos": {
72
+ "first_name": "",
73
+ "last_name": "",
74
+ "phone_number": "",
75
+ "email_address": "",
76
+ "demo_user": "",
77
+ "demo_password": "",
78
+ "notes": ""
79
+ }
80
+ }
81
+ }
82
+ File.write(File.join(folder, 'metadata.json'), JSON.pretty_generate(metadata))
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,102 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class UniversalMetadataMetadata
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::UniversalMetadataMetadata.your_method`
10
+ #
11
+ def self.generateMetadata(metadata, languages, metadata_folder)
12
+ FileUtils.rm_rf(metadata_folder)
13
+ FileUtils.mkdir_p(File.join(metadata_folder, 'android'))
14
+ FileUtils.mkdir_p(File.join(metadata_folder, 'review_information'))
15
+
16
+ # 1. Non localized files for iOS
17
+ self.writeFile('copyright.txt', metadata['copyright'])
18
+ self.writeFile('primary_category.txt', metadata['apple']['primary_category'])
19
+ self.writeFile('secondary_category.txt', metadata['apple']['secondary_category'])
20
+ self.writeFile('primary_first_sub_category.txt', metadata['apple']['primary_first_sub_category'])
21
+ self.writeFile('primary_second_sub_category.txt', metadata['apple']['primary_second_sub_category'])
22
+ self.writeFile('secondary_first_sub_category.txt', metadata['apple']['secondary_first_sub_category'])
23
+ self.writeFile('secondary_second_sub_category.txt', metadata['apple']['secondary_second_sub_category'])
24
+
25
+ self.writeFile('review_information/first_name.txt', metadata['apple']['reviewInfos']['first_name'])
26
+ self.writeFile('review_information/last_name.txt', metadata['apple']['reviewInfos']['last_name'])
27
+ self.writeFile('review_information/phone_number.txt', metadata['apple']['reviewInfos']['phone_number'])
28
+ self.writeFile('review_information/email_address.txt', metadata['apple']['reviewInfos']['email_address'])
29
+ self.writeFile('review_information/demo_user.txt', metadata['apple']['reviewInfos']['demo_user'])
30
+ self.writeFile('review_information/demo_password.txt', metadata['apple']['reviewInfos']['demo_password'])
31
+ self.writeFile('review_information/notes.txt', metadata['apple']['reviewInfos']['notes'])
32
+
33
+ for lang in languages
34
+ full_description = self.getDescription(true, lang)
35
+ short_description = self.getDescription(false, lang)
36
+
37
+ # 2. Localized files for iOS
38
+ self.writeFile(lang + '/name.txt', self.localized(metadata['name'], lang))
39
+ self.writeFile(lang + '/subtitle.txt', self.localized(metadata['subtitle'], lang))
40
+ self.writeFile(lang + '/privacy_url.txt', self.localized(metadata['privacy_url'], lang))
41
+ self.writeFile(lang + '/description.txt', full_description)
42
+ self.writeFile(lang + '/keywords.txt', self.localized(metadata['apple']['keywords'], lang))
43
+ self.writeFile(lang + '/release_notes.txt', self.getReleaseNotes(lang))
44
+ self.writeFile(lang + '/support_url.txt', self.localized(metadata['apple']['support_url'], lang))
45
+ self.writeFile(lang + '/marketing_url.txt', self.localized(metadata['apple']['marketing_url'], lang))
46
+ self.writeFile(lang + '/promotional_text.txt', self.localized(metadata['apple']['promotional_text'], lang))
47
+
48
+ # 3. Localized files for Android
49
+ self.writeFile('android/' + lang + '/title.txt', self.localized(metadata['name'], lang))
50
+ self.writeFile('android/' + lang + '/full_description.txt', full_description)
51
+ self.writeFile('android/' + lang + '/short_description.txt', short_description)
52
+ self.writeFile('android/' + lang + '/video.txt', "")
53
+ self.writeFile('android/' + lang + '/changelogs/default.txt', self.getReleaseNotes(lang))
54
+ end
55
+ end
56
+
57
+ def self.writeFile(file, content)
58
+ file = 'fastlane/metadata/' + file
59
+ FileUtils.mkdir_p(File.dirname(file))
60
+ File.open(file, "w") { |f| f.write content }
61
+ end
62
+
63
+ def self.getDescription(full, lang)
64
+ folder = 'fastlane/universal-metadata/description/'
65
+ file = full ? '/full_description.txt' : '/short_description.txt'
66
+ if File.exists?(folder + lang + file) then
67
+ return File.read(folder + lang + file)
68
+ elsif File.exists?(folder + 'default' + file) then
69
+ return File.read(folder + 'default' + file)
70
+ else
71
+ puts "Warning: " + lang + ' or default not found for description'
72
+ return ""
73
+ end
74
+ end
75
+
76
+ def self.getReleaseNotes(lang)
77
+ folder = File.join('fastlane/universal-metadata/release-notes/')
78
+
79
+ if File.exists?(File.join(folder, lang + '.txt')) then
80
+ return File.read(File.join(folder, lang + '.txt'))
81
+ elsif File.exists?(File.join(folder, 'default.txt')) then
82
+ return File.read(File.join(folder, 'default.txt'))
83
+ else
84
+ puts "Warning: " + lang + ' or default not found for release-notes'
85
+ return ""
86
+ end
87
+ end
88
+
89
+ def self.localized(metadata, lang)
90
+ if metadata.key?(lang) then
91
+ return metadata[lang]
92
+ elsif metadata.key?('default') then
93
+ return metadata['default']
94
+ else
95
+ puts "Warning: " + lang + ' or default not found in ' + metadata.to_s
96
+ return ""
97
+ end
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,109 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class UniversalMetadataScreenshots
8
+ def self.generateScreenshots(languages, params, metadata_folder)
9
+ if params[:skip_screenshots] then
10
+ UI.message("Skip screenshots")
11
+ return
12
+ end
13
+
14
+ ios_screenshot_folder = params[:ios_screenshots_dir]
15
+ FileUtils.rm_rf(ios_screenshot_folder)
16
+ FileUtils.mkdir_p(ios_screenshot_folder)
17
+
18
+ screenSizes = {
19
+ # Apple
20
+ "iPhone-6_5" => { "isTablet" => false, "width" => 1242, "height" => 2688, "isApple" => true },
21
+ # "iPhone-6_7" => { "isTablet" => false, "width" => 1290, "height" => 2796, "isApple" => true },
22
+ # "iPhone-6_1" => { "isTablet" => false, "width" => 1179, "height" => 2556, "isApple" => true },
23
+ # "iPhone-5_8" => { "isTablet" => false, "width" => 1170, "height" => 2532, "isApple" => true },
24
+ "iPhone-5_5" => { "isTablet" => false, "width" => 1242, "height" => 2208, "isApple" => true },
25
+ "iPad-12_9" => { "isTablet" => true, "width" => 2048, "height" => 2732, "isApple" => true },
26
+ "ipadPro129" => { "isTablet" => true, "width" => 2048, "height" => 2732, "isApple" => true },
27
+ # "iPad-11" => { "isTablet" => true, "width" => 1668, "height" => 2388, "isApple" => true },
28
+
29
+ # Android
30
+ "android-5_8" => { "isTablet" => false, "width" => 1242, "height" => 2208, "isApple" => false, "folder" => 'phoneScreenshots' },
31
+ "android-7" => { "isTablet" => true, "width" => 2048, "height" => 2732, "isApple" => false, "folder" => 'sevenInchScreenshots' },
32
+ "android-11" => { "isTablet" => true, "width" => 2048, "height" => 2732, "isApple" => false, "folder" => 'tenInchScreenshots' },
33
+ }
34
+
35
+ for lang in languages
36
+ for screenshot in self.getScreenshotFiles(lang) do
37
+ tags = self.getScreenshotTags(screenshot)
38
+
39
+ screenSizes.each do |deviceName, deviceInfo|
40
+ if tags["isApple"] == deviceInfo["isApple"] || tags["isAndroid"] != deviceInfo["isApple"] then
41
+ if deviceInfo["isApple"] then
42
+ targetFolder = File.join(ios_screenshot_folder, lang)
43
+ else
44
+ targetFolder = File.join(metadata_folder, 'android', lang, 'images', deviceInfo["folder"])
45
+ end
46
+
47
+ FileUtils.mkdir_p(targetFolder)
48
+ if params[:skip_screenshots_resize] then
49
+ FileUtils.cp(screenshot, File.join(targetFolder, File.basename(screenshot)))
50
+ else
51
+ if deviceInfo["isTablet"] == tags["isTablet"] then
52
+ self.resizeImage(
53
+ screenshot,
54
+ File.join(targetFolder, deviceName + '-' + File.basename(screenshot)),
55
+ deviceInfo["width"],
56
+ deviceInfo["height"],
57
+ tags["resizeContain"],
58
+ deviceInfo["isApple"]
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.getScreenshotTags(file)
69
+ file = File.basename(file).downcase
70
+ tags = {
71
+ "isTablet" => file.include?('tablet'),
72
+ "resizeContain" => file.include?('contain'),
73
+ "isApple" => file.include?('apple') || !file.include?('android'),
74
+ "isAndroid" => file.include?('android') || !file.include?('apple'),
75
+ }
76
+ tags
77
+ end
78
+
79
+ def self.getScreenshotFiles(lang)
80
+ folder = 'fastlane/universal-metadata/screenshots/'
81
+ if File.exists?(folder + lang) then
82
+ return Dir.entries(folder + lang).map { |file| folder + lang + '/' + file }
83
+ elsif File.exists?(folder + 'default') then
84
+ return Dir.entries(folder + 'default').map { |file| folder + 'default/' + file }
85
+ else
86
+ puts "Warning: " + lang + ' or default not found for screenshots'
87
+ return []
88
+ end
89
+ end
90
+
91
+ def self.resizeImage(source, target, width, height, resizeContain, addBorder)
92
+ size = width.to_s + 'x' + height.to_s
93
+
94
+ # Add ^ just after the size make the resizeMode to cover (default contain)
95
+ cmd = 'magick convert "'+source+'" -resize ' + size + (resizeContain ? '' : '^')
96
+ if addBorder then
97
+ cmd += ' -gravity center -extent '+size
98
+ end
99
+ cmd += ' "'+target+'"'
100
+
101
+ result = system(cmd)
102
+ if !result then
103
+ puts "Warning: Error resizing file " + source
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module UniversalMetadata
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/universal_metadata/version'
2
+
3
+ module Fastlane
4
+ module UniversalMetadata
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::UniversalMetadata.all_classes.each do |current|
15
+ require current
16
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-universal_metadata
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - La Gregance
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-02-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fastlane
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.211.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.211.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec_junit_formatter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.12.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.12.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-require_tools
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email: ''
155
+ executables: []
156
+ extensions: []
157
+ extra_rdoc_files: []
158
+ files:
159
+ - LICENSE
160
+ - README.md
161
+ - lib/fastlane/plugin/universal_metadata.rb
162
+ - lib/fastlane/plugin/universal_metadata/actions/universal_metadata_action.rb
163
+ - lib/fastlane/plugin/universal_metadata/helper/universal_metadata_const.rb
164
+ - lib/fastlane/plugin/universal_metadata/helper/universal_metadata_init.rb
165
+ - lib/fastlane/plugin/universal_metadata/helper/universal_metadata_metadata.rb
166
+ - lib/fastlane/plugin/universal_metadata/helper/universal_metadata_screenshots.rb
167
+ - lib/fastlane/plugin/universal_metadata/version.rb
168
+ homepage: https://github.com/LaGregance/fastlane-plugin-universal_metadata
169
+ licenses:
170
+ - MIT
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '2.6'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubygems_version: 3.1.6
188
+ signing_key:
189
+ specification_version: 4
190
+ summary: Unify metadata files for iOS & Android
191
+ test_files: []