Skip to content

Commit 431b1ed

Browse files
authored
Merge pull request #429 from wordpress-mobile/screenshots/promo-screenshots-improvements
Promo Screenshots action improvements
2 parents b6ffaac + f3aa1dc commit 431b1ed

File tree

6 files changed

+91
-43
lines changed

6 files changed

+91
-43
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ _None_
1010

1111
### New Features
1212

13-
* Allow `android_firebase_test` to not crash on failure, letting the caller do custom failure handling (e.g. Buildkite Annotations, etc) on their side. [#430]
13+
- Allow `android_firebase_test` to not crash on failure, letting the caller do custom failure handling (e.g. Buildkite Annotations, etc) on their side. [#430]
14+
- `promo_screenshots` now checks that the fonts—referenced via `font-family` in all the stylesheets referenced in the config file—are installed before starting, and prompt to install them if they are not. This check is enabled by default now but can be disabled/skipped if it causes any issue. [#429]
15+
- `promo_screenshots` now supports config files to be written in `YAML` in addition to still supporting `JSON`. [#429]
1416

1517
### Bug Fixes
1618

17-
_None_
19+
- Fix deprecation warning in `RMagick` call used by `promo_screenshots` action. [#429]
1820

1921
### Internal Changes
2022

Gemfile.lock

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ PATH
99
diffy (~> 3.3)
1010
git (~> 1.3)
1111
google-cloud-storage (~> 1.31)
12-
jsonlint (~> 0.3)
1312
nokogiri (~> 1.11)
1413
octokit (~> 4.18)
1514
parallel (~> 1.14)
@@ -264,9 +263,6 @@ GEM
264263
concurrent-ruby (~> 1.0)
265264
jmespath (1.6.1)
266265
json (2.6.2)
267-
jsonlint (0.3.0)
268-
oj (~> 3)
269-
optimist (~> 3)
270266
jwt (2.5.0)
271267
kramdown (2.3.1)
272268
rexml
@@ -292,9 +288,7 @@ GEM
292288
octokit (4.21.0)
293289
faraday (>= 0.9)
294290
sawyer (~> 0.8.0, >= 0.5.3)
295-
oj (3.13.20)
296291
open4 (1.3.4)
297-
optimist (3.0.1)
298292
options (2.3.2)
299293
optparse (0.1.1)
300294
os (1.1.4)

docs/screenshot-compositor.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,17 @@ By default, this tool will prompt the developer before overwriting any existing
3737

3838
**`config_file`** (optional)
3939

40-
The tool uses a `.json` file for configuration. By default, the tool will attempt to use a file called `screenshots.json` in the same directory as the `Fastfile`. If your file has a different name, you'll need to use this argument.
40+
The tool uses a `.json` or `.yaml` file for configuration. By default, the tool will attempt to use a file called `screenshots.json` in the same directory as the `Fastfile`. If your file has a different name, you'll need to use this argument.
4141

4242
## Sample Implementations
4343

4444
The following sample implementations provide a good example of how to create a configuration file:
4545

46-
- [WordPress Android](https://github.com/wordpress-mobile/WordPress-Android/blob/trunk/fastlane/screenshots.json)
46+
- [WordPress Android](https://github.com/wordpress-mobile/WordPress-Android/blob/trunk/fastlane/screenshots/wordpress-config.json)
4747

4848
## Creating a configuration file
4949

50-
The `.json` configuration file contains all the information needed for the compositor to do its work. It has three main sections:
50+
The `.json` (or `.yaml`) configuration file contains all the information needed for the compositor to do its work. It has three main sections:
5151

5252
- Preamble
5353
- Devices
@@ -69,7 +69,7 @@ This key defines the default background color of each screenshot. This must be a
6969

7070
**`stylesheet`**
7171

72-
This key defines the default stylesheet used for text formatting. It should be a path to the stylesheet, either absolute, or relative to the configuration file.
72+
This key defines the default stylesheet used for text formatting. It should be a path to the stylesheet, either absolute, or relative to the `fastlane` folder.
7373

7474
A sample stylesheet looks like this:
7575

@@ -105,7 +105,7 @@ The device list specifies a set of reusable device frames for the compositor. Th
105105
"font_size": "70px",
106106
"screenshot_size": [740, 1480],
107107
"screenshot_offset": [170, 440],
108-
"device_frame": "playstoreres/assets/pixel-2-xl.svg",
108+
"device_frame": "screenshots/assets/pixel-2-xl.svg",
109109
"device_frame_size": [833, 1708],
110110
"device_frame_offset": [124, 317]
111111
},
@@ -143,7 +143,7 @@ Sets the origin of the original screenshot image. This must be provided as an ar
143143

144144
**`device_frame`**
145145

146-
A path to the device frame image (if applicable). This path can be absolute or relative to the configuration file.
146+
A path to the device frame image (if applicable). This path can be absolute or relative to the `fastlane` folder.
147147

148148
**`device_frame_size`**
149149

@@ -165,17 +165,17 @@ Each entry looks something like this:
165165
```json
166166
{
167167
"device": "Pixel 2 XL",
168-
"text": "playstoreres/metadata/%s/play_store_screenshot_7.html",
168+
"text": "metadata/%s/play_store_screenshot_7.html",
169169
"screenshot": "images/phoneScreenshots/1-build-and-manage-your-website.png",
170-
"background": "playstoreres/assets/backgrounds/01-background-phone.png",
170+
"background": "screenshots/assets/backgrounds/01-background-phone.png",
171171
"attachments": [
172172
{
173-
"file": "playstoreres/assets/attachments/01-phone-website.png",
173+
"file": "screenshots/assets/attachments/01-phone-website.png",
174174
"size": [740, 1480],
175175
"position": [170, 441]
176176
},
177177
{
178-
"file": "playstoreres/assets/attachments/01-phone-photos.png",
178+
"file": "screenshots/assets/attachments/01-phone-photos.png",
179179
"size": [918, 975],
180180
"position": [78, 832]
181181
}
@@ -194,7 +194,7 @@ Allows you to customize the exported filename for this entry.
194194

195195
**`background`** (optional)
196196

197-
Specifies the path to a background image for this entry. This path can be absolute or relative to the configuration file. The background image **will not** be automatically resized, and it will be placed at `(0,0)`. If not provided, the image will use the background color defined in the preamble.
197+
Specifies the path to a background image for this entry. This path can be absolute, or relative to the `fastlane` folder. The background image **will not** be automatically resized, and it will be placed at `(0,0)`. If not provided, the image will use the background color defined in the preamble.
198198

199199
**`screenshot`** (optional)
200200

@@ -253,7 +253,7 @@ Text attachments use the following keys:
253253

254254
**`text`**
255255

256-
Specifies the path to the text file for this attachment. The path should be specified relative to the configuration file, and can contain the `{locale}` placeholder to allow for localization.
256+
Specifies the path to the text file for this attachment. The path should be specified relative to the `fastlane` folder, and can contain the `{locale}` placeholder to allow for localization.
257257

258258
**`size`**
259259

@@ -269,12 +269,12 @@ Specifies the font size for the text of this attachment. If this number is too l
269269

270270
**`stylesheet`**
271271

272-
Specifies a stylesheet used for this attachment. It should contain a path to the stylesheet, either absolute, or relative to the configuration file.
272+
Specifies a stylesheet used for this attachment. It should contain a path to the stylesheet, either absolute, or relative to the `fastlane` folder.
273273

274274
**Image Attachment**
275275
```json
276276
{
277-
"file": "playstoreres/assets/attachments/01-image-name.png",
277+
"file": "screenshots/assets/attachments/01-image-name.png",
278278
"size": [2107, 1414],
279279
"position": [446, 586]
280280
}
@@ -284,7 +284,7 @@ Image attachments use the following keys:
284284

285285
**`file`**
286286

287-
Specifies the path to the attachment image. The path should be specified relative to the configuration file, and can contain the `{locale}` placeholder to allow for localization.
287+
Specifies the path to the attachment image. The path should be specified relative to the `fastlane` folder, and can contain the `{locale}` placeholder to allow for localization.
288288

289289
**`size`**
290290

fastlane-plugin-wpmreleasetoolkit.gemspec

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ Gem::Specification.new do |spec|
3030
spec.add_dependency 'chroma', '0.2.0'
3131
spec.add_dependency 'diffy', '~> 3.3'
3232
spec.add_dependency 'git', '~> 1.3'
33-
spec.add_dependency 'jsonlint', '~> 0.3'
3433
spec.add_dependency 'nokogiri', '~> 1.11' # Needed for AndroidLocalizeHelper
3534
spec.add_dependency 'octokit', '~> 4.18'
3635
spec.add_dependency 'parallel', '~> 1.14'

lib/fastlane/plugin/wpmreleasetoolkit/actions/common/promo_screenshots_action.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ def self.run(params)
1111
UI.message "#{self.check_path(params[:orig_folder])} Original Screenshot Source: #{params[:orig_folder]}"
1212
UI.message "#{self.check_path(params[:metadata_folder])} Translation source: #{params[:metadata_folder]}"
1313

14-
config = helper.read_json(params[:config_file])
14+
config = helper.read_config(params[:config_file])
15+
16+
helper.check_fonts_installed!(config: config) unless params[:skip_font_check]
1517

1618
translationDirectories = subdirectories_for_path(params[:metadata_folder])
1719
imageDirectories = subdirectories_for_path(params[:orig_folder])
@@ -141,31 +143,34 @@ def self.available_options
141143
env_name: 'PROMOSS_ORIG',
142144
description: 'The directory containing the original screenshots',
143145
optional: false,
144-
is_string: true),
146+
type: String),
145147
FastlaneCore::ConfigItem.new(key: :output_folder,
146148
env_name: 'PROMOSS_OUTPUT',
147149
description: 'The path of the folder to save the promo screenshots',
148150
optional: false,
149-
is_string: true),
150-
151+
type: String),
151152
FastlaneCore::ConfigItem.new(key: :metadata_folder,
152153
env_name: 'PROMOSS_METADATA_FOLDER',
153154
description: 'The directory containing the translation data',
154155
optional: false,
155-
is_string: true),
156-
156+
type: String),
157157
FastlaneCore::ConfigItem.new(key: :config_file,
158158
env_name: 'PROMOSS_CONFIG_FILE',
159159
description: 'The path to the file containing the promo screenshot configuration',
160160
optional: true,
161-
is_string: true,
161+
type: String,
162162
default_value: 'screenshots.json'),
163-
164163
FastlaneCore::ConfigItem.new(key: :force,
165164
env_name: 'PROMOSS_FORCE_CREATION',
166165
description: 'Overwrite existing promo screenshots without asking first?',
167166
optional: true,
168-
is_string: false,
167+
type: Boolean,
168+
default_value: false),
169+
FastlaneCore::ConfigItem.new(key: :skip_font_check,
170+
env_name: 'PROMOSS_SKIP_FONT_CHECK',
171+
description: 'Skip the check verifying that needed fonts are installed and active',
172+
optional: true,
173+
type: Boolean,
169174
default_value: false),
170175
]
171176
end

lib/fastlane/plugin/wpmreleasetoolkit/helper/promo_screenshots_helper.rb

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
end
88
require 'json'
99
require 'tempfile'
10+
require 'open3'
1011
require 'optparse'
1112
require 'pathname'
1213
require 'progress_bar'
1314
require 'parallel'
14-
require 'jsonlint'
1515
require 'chroma'
1616
require 'securerandom'
1717

@@ -32,17 +32,65 @@ def initialize
3232
UI.user_error!('`drawText` not found – install it using `brew install automattic/build-tools/drawText`.') unless system('command -v drawText')
3333
end
3434

35-
def read_json(configFilePath)
35+
def read_config(configFilePath)
3636
configFilePath = resolve_path(configFilePath)
3737

3838
begin
39-
return JSON.parse(open(configFilePath).read)
40-
rescue
41-
linter = JsonLint::Linter.new
42-
linter.check(configFilePath)
43-
linter.display_errors
39+
# NOTE: While JSON is a subset of YAML and thus YAML.load_file would technically cover both cases at once, in practice
40+
# `JSON.parse` is more lenient with JSON files than `YAML.load_file` is — especially, it accepts `// comments` in the
41+
# JSON file, despite this not being allowed in the spec — hence why we still try with `JSON.parse` for `.json` files.
42+
return File.extname(configFilePath) == '.json' ? JSON.parse(File.read(configFilePath)) : YAML.load_file(configFilePath)
43+
rescue StandardError => e
44+
UI.error(e)
45+
UI.user_error!('Invalid JSON/YAML configuration. Please lint your config file to check for syntax errors.')
46+
end
47+
end
4448

45-
UI.user_error!('Invalid JSON configuration. See errors in log.')
49+
# Checks that all required fonts are installed
50+
# - Visits the JSON config to find all the stylesheets referenced in it
51+
# - For each stylesheet, extract the first font of each `font-family` attribute found
52+
# - Finally, for each of those fonts, check that they exist and are activated.
53+
#
54+
# @param [Hash] config The promo screenshots configuration, as returned by #read_config
55+
# @raise [UserError] Raises if at least one font is missing.
56+
#
57+
def check_fonts_installed!(config:)
58+
# Find all stylesheets in the config
59+
all_stylesheets = ([config['stylesheet']] + config['entries'].flat_map do |entry|
60+
entry['attachments']&.map { |att| att['stylesheet'] }
61+
end).compact.uniq
62+
63+
# Parse the first font in each `font-family` attribute found in all found CSS files.
64+
# Only the first in each `font-family` font list matters, as others are fallbacks we don't want to use anyway.
65+
font_families = all_stylesheets.flat_map do |s|
66+
File.readlines(s).flat_map do |line|
67+
attr = line.match(/font-family: (.*);/)&.captures&.first
68+
attr.split(',').first.strip.gsub(/'(.*)'/, '\1').gsub(/"(.*)"/, '\1') unless attr.nil?
69+
end
70+
end.compact.uniq
71+
72+
# Verify that all fonts exists and are active—using a small swift script as there's no nice CLI for that
73+
swift_script = <<~SWIFT
74+
import AppKit
75+
76+
var exitCode: Int32 = 0
77+
for fontName in CommandLine.arguments.dropFirst() {
78+
if NSFont(name: fontName, size: NSFont.systemFontSize) != nil {
79+
print(" ✅ Font \\"\\(fontName)\\" found and active")
80+
} else {
81+
print(" ❌ Font \\"\\(fontName)\\" not found, it is either not installed or disabled. Please install it using FontBook first.")
82+
exitCode = 1
83+
}
84+
}
85+
exit(exitCode)
86+
SWIFT
87+
88+
Tempfile.create(['fonts-check-', '.swift']) do |f|
89+
f.write(swift_script)
90+
f.close
91+
oe, s = Open3.capture2e('/usr/bin/env', 'xcrun', 'swift', f.path, *font_families)
92+
UI.command_output(oe)
93+
UI.user_error!('Some fonts required by your stylesheets are missing. Please install them and try again.') unless s.success?
4694
end
4795
end
4896

@@ -343,8 +391,8 @@ def crop_image(original, x_position, y_position, width, height)
343391
def open_image(path)
344392
path = resolve_path(path)
345393

346-
Magick::Image.read(path) do
347-
self.background_color = 'transparent'
394+
Magick::Image.read(path) do |image|
395+
image.background_color = 'transparent'
348396
end.first
349397
end
350398

0 commit comments

Comments
 (0)