Skip to content

Commit f8000b5

Browse files
authored
Merge pull request devicons#412 from devicons/TB_peekUpgrade
Make the peek script (bot:peek) take screenshot of individual files
2 parents 8687cec + 125ba1f commit f8000b5

File tree

9 files changed

+193
-58
lines changed

9 files changed

+193
-58
lines changed

.github/scripts/build_assets/SeleniumRunner.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,13 @@ def upload_icomoon(self, icomoon_json_path: str):
118118

119119
print("JSON file uploaded.")
120120

121-
def upload_svgs(self, svgs: List[str]):
121+
def upload_svgs(self, svgs: List[str], screenshot_folder: str=""):
122122
"""
123123
Upload the SVGs provided in folder_info
124124
:param svgs: a list of svg Paths that we'll upload to icomoon.
125+
:param screenshot_folder: the name of the screenshot_folder. If
126+
the value is provided, it means the user want to take a screenshot
127+
of each icon.
125128
"""
126129
try:
127130
print("Uploading SVGs...")
@@ -133,17 +136,20 @@ def upload_svgs(self, svgs: List[str]):
133136

134137
self.click_hamburger_input()
135138

136-
for svg in svgs:
139+
for i in range(len(svgs)):
137140
import_btn = self.driver.find_element_by_css_selector(
138141
"li.file input[type=file]"
139142
)
140-
import_btn.send_keys(svg)
141-
print(f"Uploaded {svg}")
143+
import_btn.send_keys(svgs[i])
144+
print(f"Uploaded {svgs[i]}")
142145
self.test_for_possible_alert(self.SHORT_WAIT_IN_SEC, "Dismiss")
143-
self.remove_color_from_icon()
146+
self.click_on_just_added_icon(screenshot_folder, i)
144147

145148
# take a screenshot of the icons that were just added
146-
self.driver.save_screenshot("new_icons.png");
149+
new_icons_path = str(Path(screenshot_folder, "new_icons.png").resolve())
150+
self.driver.save_screenshot(new_icons_path);
151+
152+
# select all the svgs so that the newly added svg are part of the collection
147153
self.click_hamburger_input()
148154
select_all_button = WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
149155
ec.element_to_be_clickable((By.XPATH, "//button[text()='Select All']"))
@@ -191,45 +197,50 @@ def test_for_possible_alert(self, wait_period: float, btn_text: str):
191197
)
192198
dismiss_btn.click()
193199
except SeleniumTimeoutException:
194-
pass
200+
pass # nothing found => everything is good
195201

196-
def remove_color_from_icon(self):
202+
def click_on_just_added_icon(self, screenshot_folder: str, index: int):
197203
"""
198-
Remove the color from the most recent uploaded icon.
199-
:return: None.
204+
Click on the most recently added icon so we can remove the colors
205+
and take a snapshot if needed.
200206
"""
201207
try:
202208
recently_uploaded_icon = WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
203209
ec.element_to_be_clickable((By.XPATH, "//div[@id='set0']//mi-box[1]//div"))
204210
)
205211
recently_uploaded_icon.click()
206-
except Exception as e:
207-
self.close()
208-
raise e
209212

210-
try:
211-
color_tab = WebDriverWait(self.driver, self.SHORT_WAIT_IN_SEC).until(
212-
ec.element_to_be_clickable((By.CSS_SELECTOR, "div.overlayWindow i.icon-droplet"))
213-
)
214-
color_tab.click()
213+
self.remove_color_from_icon()
215214

216-
remove_color_btn = self.driver \
217-
.find_element_by_css_selector("div.overlayWindow i.icon-droplet-cross")
218-
remove_color_btn.click()
219-
except SeleniumTimeoutException:
220-
pass
221-
except Exception as e:
222-
self.close()
223-
raise e
215+
if screenshot_folder:
216+
screenshot_path = str(Path(screenshot_folder, f"screenshot_{index}.png").resolve())
217+
self.driver.save_screenshot(screenshot_path)
218+
print("Took screenshot and saved it at " + screenshot_path)
224219

225-
try:
226220
close_btn = self.driver \
227221
.find_element_by_css_selector("div.overlayWindow i.icon-close")
228222
close_btn.click()
229223
except Exception as e:
230224
self.close()
231225
raise e
232226

227+
def remove_color_from_icon(self):
228+
"""
229+
Remove the color from the most recent uploaded icon.
230+
This is because some SVG have colors in them and we don't want to
231+
force contributors to remove them in case people want the colored SVGs.
232+
The color removal is also necessary so that the Icomoon-generated
233+
icons fit within one font symbol/ligiature.
234+
"""
235+
color_tab = WebDriverWait(self.driver, self.SHORT_WAIT_IN_SEC).until(
236+
ec.element_to_be_clickable((By.CSS_SELECTOR, "div.overlayWindow i.icon-droplet"))
237+
)
238+
color_tab.click()
239+
240+
remove_color_btn = self.driver \
241+
.find_element_by_css_selector("div.overlayWindow i.icon-droplet-cross")
242+
remove_color_btn.click()
243+
233244
def download_icomoon_fonts(self, zip_path: Path):
234245
"""
235246
Download the icomoon.zip from icomoon.io.

.github/scripts/build_assets/util.py renamed to .github/scripts/build_assets/arg_getters.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from pathlib import Path
21
from argparse import ArgumentParser
32
from build_assets.PathResolverAction import PathResolverAction
43

5-
def get_commandline_args():
4+
5+
def get_selenium_runner_args(peek_mode=False):
66
parser = ArgumentParser(description="Upload svgs to Icomoon to create icon files.")
77

88
parser.add_argument("--headless",
@@ -26,8 +26,11 @@ def get_commandline_args():
2626
action=PathResolverAction)
2727

2828
parser.add_argument("download_path",
29-
help="The path where you'd like to download the Icomoon files to",
29+
help="The download destination of the Icomoon files",
3030
action=PathResolverAction)
3131

32+
if peek_mode:
33+
parser.add_argument("--pr_title",
34+
help="The title of the PR that we are peeking at")
3235

3336
return parser.parse_args()

.github/scripts/build_assets/filehandler.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,24 @@ def rename_extracted_files(extract_path: str):
145145
os.replace(dict_["old"], dict_["new"])
146146

147147
print("Files renamed")
148+
149+
150+
def create_screenshot_folder(dir, screenshot_name: str="screenshots/"):
151+
"""
152+
Create a screenshots folder in the dir.
153+
:param dir, the dir where we want to create the folder.
154+
:param screenshot_name, the name of the screenshot folder.
155+
:raise Exception if the dir provided is not a directory.
156+
:return the string name of the screenshot folder.
157+
"""
158+
folder = Path(dir).resolve()
159+
if not folder.is_dir():
160+
raise Exception(f"This is not a dir: {str(folder)}. \ndir must be a valid directory")
161+
162+
screenshot_folder = Path(folder, screenshot_name)
163+
try:
164+
os.mkdir(screenshot_folder)
165+
except FileExistsError:
166+
print(f"{screenshot_folder} already exist. Script will do nothing.")
167+
finally:
168+
return str(screenshot_folder)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import json
2+
import os
3+
4+
5+
if __name__ == "__main__":
6+
img_urls_list = json.loads(os.environ["IMG_URLS"])
7+
template = "![Detailed Screenshot]({})"
8+
markdown = [template.format(img_url) for img_url in img_urls_list]
9+
print("\n\n".join(markdown))
10+

.github/scripts/icomoon_build.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
# pycharm complains that build_assets is an unresolved ref
55
# don't worry about it, the script still runs
66
from build_assets.SeleniumRunner import SeleniumRunner
7-
from build_assets import filehandler, util
7+
from build_assets import filehandler, arg_getters
88

99

1010
def main():
11-
args = util.get_commandline_args()
11+
args = arg_getters.get_selenium_runner_args()
1212
new_icons = filehandler.find_new_icons(args.devicon_json_path, args.icomoon_json_path)
1313
if len(new_icons) == 0:
1414
print("No files need to be uploaded. Ending script...")

.github/scripts/icomoon_peek.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,66 @@
1+
from typing import List
2+
import re
3+
import sys
14
from selenium.common.exceptions import TimeoutException
25

36
# pycharm complains that build_assets is an unresolved ref
47
# don't worry about it, the script still runs
58
from build_assets.SeleniumRunner import SeleniumRunner
6-
from build_assets import filehandler, util
9+
from build_assets import filehandler, arg_getters
710

811

912
def main():
10-
args = util.get_commandline_args()
13+
args = arg_getters.get_selenium_runner_args(True)
1114
new_icons = filehandler.find_new_icons(args.devicon_json_path, args.icomoon_json_path)
15+
16+
# get only the icon object that has the name matching the pr title
17+
filtered_icons = find_object_added_in_this_pr(new_icons, args.pr_title)
18+
1219
if len(new_icons) == 0:
13-
print("No files need to be uploaded. Ending script...")
14-
return
20+
sys.exit("No files need to be uploaded. Ending script...")
21+
22+
if len(filtered_icons) == 0:
23+
message = "No icons found matching the icon name in the PR's title.\n" \
24+
"Ensure that the PR title matches the convention here: \n" \
25+
"https://github.com/devicons/devicon/blob/master/CONTRIBUTING.md#overview.\n" \
26+
"Ending script...\n"
27+
sys.exit(message)
1528

1629
# print list of new icons
1730
print("List of new icons:", *new_icons, sep = "\n")
31+
print("Icons being uploaded:", *filtered_icons, sep = "\n", end='\n\n')
1832

1933
runner = None
2034
try:
2135
runner = SeleniumRunner(args.download_path, args.geckodriver_path, args.headless)
22-
svgs = filehandler.get_svgs_paths(new_icons, args.icons_folder_path)
23-
runner.upload_svgs(svgs)
36+
svgs = filehandler.get_svgs_paths(filtered_icons, args.icons_folder_path)
37+
screenshot_folder = filehandler.create_screenshot_folder("./")
38+
runner.upload_svgs(svgs, screenshot_folder)
2439
print("Task completed.")
2540
except TimeoutException as e:
41+
print("Selenium Time Out Error: ", e.stacktrace, sep='\n')
42+
except Exception as e:
2643
print(e)
27-
print(e.stacktrace)
2844
finally:
2945
runner.close()
3046

3147

48+
def find_object_added_in_this_pr(icons: List[dict], pr_title: str):
49+
"""
50+
Find the icon name from the PR title.
51+
:param icons, a list of the font objects found in the devicon.json.
52+
:pr_title, the title of the PR that this workflow was called on.
53+
:return a list containing the dictionary with the "name"
54+
entry's value matching the name in the pr_title.
55+
If none can be found, return an empty list.
56+
"""
57+
try:
58+
pattern = re.compile(r"(?<=^new icon: )\w+ (?=\(.+\))", re.I)
59+
icon_name = pattern.findall(pr_title)[0].lower().strip() # should only have one match
60+
return [icon for icon in icons if icon["name"] == icon_name]
61+
except IndexError: # there are no match in the findall()
62+
return []
63+
64+
3265
if __name__ == "__main__":
3366
main()

.github/workflows/build_icons.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,28 @@ jobs:
1919
pip install -r ./.github/scripts/requirements.txt
2020
npm install
2121
- name: Executing build and create fonts via icomoon
22-
run: npm run build
22+
run: >
23+
python ./.github/scripts/icomoon_build.py
24+
./.github/scripts/build_assets/geckodriver-v0.27.0-win64/geckodriver.exe ./icomoon.json
25+
./devicon.json ./icons ./ --headless
2326
- name: Upload geckodriver.log for debugging purposes
2427
uses: actions/upload-artifact@v2
25-
if: ${{failure()}}
28+
if: failure()
2629
with:
2730
name: geckodriver-log
2831
path: ./geckodriver.log
2932
- name: Build devicon.min.css
30-
if: ${{ success() }}
33+
if: success()
3134
run: npm run build-css
3235
- name: Upload screenshot of the newly made icons
3336
id: imgur_step
34-
uses: devicons/public-upload-to-imgur@v1
35-
if: ${{success()}}
37+
uses: devicons/public-upload-to-imgur@v2
38+
if: success()
3639
with:
37-
img_path: ./new_icons.png
40+
path: ./new_icons.png
3841
client_id: ${{secrets.IMGUR_CLIENT_ID}}
3942
- name: Create Pull Request
40-
if: ${{ success() }}
43+
if: success()
4144
uses: peter-evans/create-pull-request@v3
4245
env:
4346
MESSAGE: |
@@ -53,5 +56,5 @@ jobs:
5356
base: 'master'
5457
commit-message: 'Built new icons, icomoon.json and devicon.css'
5558
title: 'bot:build new icons, icomoon.json and devicon.css'
56-
body: ${{ format(env.MESSAGE, steps.imgur_step.outputs.imgur_url ) }}
59+
body: ${{ format(env.MESSAGE, fromJSON(steps.imgur_step.outputs.imgur_url)[0] ) }}
5760
delete-branch: true

0 commit comments

Comments
 (0)