1
0
mirror of https://github.com/konpa/devicon.git synced 2025-08-11 17:14:27 +02:00

New Feature: Upgrade Peekbot (#966)

* Moved stroke detection down

* Add code to retry no connection in peekbot

* Change upload artifact to earlier version

* Clean up logging msg

* Add ability to customize port number

* Update Selenium and geckodriver

* Move upload-artifact version to 2.2.4

* Add logging for webdriver retry

* Add color checking to peek-bot
This commit is contained in:
Thomas Bui
2021-12-21 12:33:41 -08:00
committed by GitHub
parent 34a54bcf1a
commit 2a8c499605
9 changed files with 112 additions and 26 deletions

View File

@@ -3,4 +3,4 @@ Proxy for using W3C WebDriver compatible clients to interact with Gecko-based br
This program provides the HTTP API described by the WebDriver protocol to communicate with Gecko browsers, such as Firefox.
It translates calls into the Marionette remote protocol by acting as a proxy between the local- and remote ends.
This application was taken from: https://github.com/mozilla/geckodriver/releases/tag/v0.29.1
This application was taken from: https://github.com/mozilla/geckodriver/releases/tag/v0.30.0

View File

@@ -5,17 +5,18 @@ from build_assets.selenium_runner.SeleniumRunner import SeleniumRunner
from build_assets.selenium_runner.enums import IcomoonPage, IcomoonAlerts
class PeekSeleniumRunner(SeleniumRunner):
def peek(self, svgs: List[str], screenshot_folder: str):
def peek(self, svgs: List[str], screenshot_folder: str, icon_info: dict):
"""
Upload the SVGs and peek at how Icomoon interpret its SVGs and
font versions.
:param svgs: a list of svg Paths that we'll upload to icomoon.
:param screenshot_folder: the name of the screenshot_folder.
:param icon_info: a dictionary containing info on an icon. Taken from the devicon.json.
:return an array of svgs with strokes as strings. These show which icon
contains stroke.
"""
messages = self.peek_svgs(svgs, screenshot_folder)
self.peek_icons(svgs, screenshot_folder)
self.peek_icons(screenshot_folder, icon_info)
return messages
def peek_svgs(self, svgs: List[str], screenshot_folder: str):
@@ -61,10 +62,11 @@ class PeekSeleniumRunner(SeleniumRunner):
print("Finished peeking the svgs...")
return svgs_with_strokes
def peek_icons(self, svgs: List[str], screenshot_folder: str):
def peek_icons(self, screenshot_folder: str, icon_info: dict):
"""
Peek at the icon versions of the SVGs that were uploaded.
:param screenshot_folder: the name of the screenshot_folder.
:param icon_info: a dictionary containing info on an icon. Taken from the devicon.json.
"""
print("Begin peeking at the icons...")
# ensure all icons in the set is selected.
@@ -85,7 +87,7 @@ class PeekSeleniumRunner(SeleniumRunner):
main_content = self.driver.find_element_by_xpath(main_content_xpath)
main_content.screenshot(new_icons_path);
# go downward so we get the oldest icon first
# go in reverse order so we get the oldest icon first
icon_divs_xpath = f'//div[@id="glyphSet0"]/div'
icon_divs = self.driver.find_elements_by_xpath(icon_divs_xpath)
icon_divs.reverse()
@@ -98,6 +100,23 @@ class PeekSeleniumRunner(SeleniumRunner):
Path(screenshot_folder, f"new_icon_{i}.png").resolve()
)
icon_div.screenshot(icon_screenshot)
i += 1
# test the colors
style = "#glyphSet0 span:first-of-type {color: " + icon_info["color"] + "}"
script = f"document.styleSheets[0].insertRule('{style}', 0)"
self.driver.execute_script(script)
i = 0
for icon_div in icon_divs:
if not icon_div.is_displayed():
continue
icon_screenshot = str(
Path(screenshot_folder, f"new_colored_icon_{i}.png").resolve()
)
icon_div.screenshot(icon_screenshot)
i += 1
print("Finished peeking the icons...")

View File

@@ -1,6 +1,8 @@
from pathlib import Path
from selenium.webdriver.common import service
from selenium.webdriver.firefox.webdriver import WebDriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
@@ -69,6 +71,11 @@ class SeleniumRunner:
IcomoonPage.GENERATE_FONT: ICOMOON_URL + "/font"
}
"""
Number of retries for creating a web driver instance.
"""
MAX_RETRY = 5
"""
The different types of alerts that this workflow will encounter.
It contains part of the text in the actual alert and buttons
@@ -126,6 +133,7 @@ class SeleniumRunner:
:raises AssertionError: if the page title does not contain
"IcoMoon App".
"""
# customize the download options
options = Options()
allowed_mime_types = "application/zip, application/gzip, application/octet-stream"
# disable prompt to download from Firefox
@@ -138,15 +146,62 @@ class SeleniumRunner:
options.headless = headless
print("Activating browser client...")
self.driver = WebDriver(options=options, executable_path=geckodriver_path)
self.driver = self.create_driver_instance(options, geckodriver_path)
self.driver.get(self.ICOMOON_URL)
assert "IcoMoon App" in self.driver.title
# wait until the whole web page is loaded by testing the hamburger input
WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
ec.element_to_be_clickable((By.XPATH, "(//i[@class='icon-menu'])[2]"))
)
print("Accessed icomoon.io")
def create_driver_instance(self, options: Options, geckodriver_path: str):
"""
Create a WebDriver instance. Isolate retrying code here to address
"no connection can be made" error.
:param options: the FirefoxOptions for the browser.
:param geckodriver_path: the path to the firefox executable.
the icomoon.zip to.
"""
retries = SeleniumRunner.MAX_RETRY
finished = False
driver = None
err_msgs = [] # keep for logging purposes
while not finished and retries > 0:
try:
# order matters, don't change the lines below
finished = True # signal we are done in case we are actually done
# customize the local server
service = None
# first retry: use 8080
# else: random
if retries == SeleniumRunner.MAX_RETRY:
service = Service(executable_path=geckodriver_path, port=8080)
else:
service = Service(executable_path=geckodriver_path)
driver = WebDriver(options=options, service=service)
except SeleniumTimeoutException as e:
# retry. This is intended to catch "no connection could be made" error
retries -= 1
finished = False # flip the var so we can retry
msg = f"Retry {retries}/{SeleniumRunner.MAX_RETRY} SeleniumTimeoutException: {e.msg}"
print(msg)
err_msgs.append(msg)
except Exception as e:
# anything else: unsure if retry works. Just end the retry
msg = f"Retry {retries}/{SeleniumRunner.MAX_RETRY} Exception: {e}"
err_msgs.append(msg)
print(msg)
break
if driver is not None:
return driver
err_msg_formatted = '\n'.join(reversed(err_msgs))
msg = f"Unable to create WebDriver Instance:\n{err_msg_formatted}"
raise Exception(msg)
def switch_toolbar_option(self, option: IcomoonOptionState):
"""
Switch the toolbar option to the option argument.
@@ -248,7 +303,7 @@ class SeleniumRunner:
except SeleniumTimeoutException:
pass # do nothing cause sometimes, the color tab doesn't appear in the site
if screenshot_folder != None and index != None:
if screenshot_folder is not None and index is not None:
edit_screen_selector = "div.overlay div.overlayWindow"
screenshot_path = str(
Path(screenshot_folder, f"new_svg_{index}.png").resolve()

View File

@@ -7,17 +7,17 @@ def main():
runner = None
try:
args = arg_getters.get_selenium_runner_args(peek_mode=True)
new_icons = filehandler.get_json_file_content(args.devicon_json_path)
all_icons = filehandler.get_json_file_content(args.devicon_json_path)
# get only the icon object that has the name matching the pr title
filtered_icon = util.find_object_added_in_pr(new_icons, args.pr_title)
filtered_icon = util.find_object_added_in_pr(all_icons, args.pr_title)
check_devicon_object(filtered_icon)
print("Icon being checked:", filtered_icon, sep = "\n", end='\n\n')
runner = PeekSeleniumRunner(args.download_path, args.geckodriver_path, args.headless)
svgs = filehandler.get_svgs_paths([filtered_icon], args.icons_folder_path, True)
screenshot_folder = filehandler.create_screenshot_folder("./")
svgs_with_strokes = runner.peek(svgs, screenshot_folder)
svgs_with_strokes = runner.peek(svgs, screenshot_folder, filtered_icon)
print("Task completed.")
message = ""
@@ -36,6 +36,7 @@ def main():
def check_devicon_object(icon: dict):
"""
Check that the devicon object added is up to standard.
:param icon: a dictionary containing info on an icon. Taken from the devicon.json.
:return a string containing the error messages if any.
"""
err_msgs = []

View File

@@ -1,2 +1,2 @@
selenium==3.141.0
selenium==4.1.0
requests==2.25.1

View File

@@ -30,7 +30,7 @@ jobs:
run: echo $PR_NUM > pr_num.txt
- name: Upload the PR number
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v2.2.4
with:
name: pr_num
path: ./pr_num.txt
@@ -41,25 +41,25 @@ jobs:
shell: cmd
run: >
python ./.github/scripts/icomoon_peek.py
./.github/scripts/build_assets/geckodriver-v0.29.1-win64/geckodriver.exe ./icomoon.json
./.github/scripts/build_assets/geckodriver-v0.30.0-win64/geckodriver.exe ./icomoon.json
./devicon.json ./icons ./ --headless --pr_title "%PR_TITLE%"
- name: Upload the err messages (created by icomoon_peek.py)
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v2.2.4
if: always()
with:
name: err_messages
path: ./err_messages.txt
- name: Upload screenshots for comments
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v2.2.4
if: success()
with:
name: screenshots
path: ./screenshots/*.png
- name: Upload geckodriver.log for debugging purposes
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v2.2.4
if: failure()
with:
name: geckodriver-log

View File

@@ -72,6 +72,14 @@ jobs:
path: ./screenshots/new_icon_*.png
client_id: ${{secrets.IMGUR_CLIENT_ID}}
- name: Upload zoomed in screenshot of the colored icons gotten from the artifacts
id: colored_icons_detailed_img_step
uses: devicons/public-upload-to-imgur@v2.2.2
if: env.PEEK_STATUS == 'success' && success()
with:
path: ./screenshots/new_colored_icon_*.png
client_id: ${{secrets.IMGUR_CLIENT_ID}}
- name: Comment on the PR about the result - Success
uses: jungwinter/comment@v1 # let us comment on a specific PR
if: env.PEEK_STATUS == 'success' && success()
@@ -80,20 +88,22 @@ jobs:
Hi there,
I'm Devicons' Peek Bot and I just peeked at the icons that you wanted to add using [icomoon.io](https://icomoon.io/app/#/select).
{0}
Here are the SVGs as intepreted by Icomoon when we upload the files:
{0}
Here are the zoomed-in screenshots of the added icons as **SVGs**:
{1}
Here are the zoomed-in screenshots of the added icons as **SVGs**. This is how Icomoon intepret the uploaded SVGs:
Here are the icons that will be generated by Icomoon:
{2}
Here are the icons that will be generated by Icomoon:
Here are the zoomed-in screenshots of the added icons as **icons**:
{3}
Here are the zoomed-in screenshots of the added icons as **icons**. This is what the font will look like:
Here are the colored versions:
{4}
You can click on the pictures and zoom on them if needed.
{5}
The maintainers will now check for:
1. The number of Glyphs matches the number of SVGs that were selected.
@@ -104,7 +114,7 @@ jobs:
Thank you for contributing to Devicon! I hope that your icons are accepted into the repository.
Note: If the images don't show up, it's probably because it has been autodeleted by Imgur after 6 months due to our API choice.
Note: If the images don't show up, it has been autodeleted by Imgur after 6 months due to our API choice.
Cheers,
Peek Bot :blush:
@@ -116,11 +126,12 @@ jobs:
${{
format(
env.MESSAGE,
steps.err_message_reader.outputs.content,
fromJSON(steps.svgs_overview_img_step.outputs.markdown_urls)[0],
join(fromJSON(steps.svgs_detailed_img_step.outputs.markdown_urls), ' '),
fromJSON(steps.icons_overview_img_step.outputs.markdown_urls)[0],
join(fromJSON(steps.icons_detailed_img_step.outputs.markdown_urls), ' ')
join(fromJSON(steps.icons_detailed_img_step.outputs.markdown_urls), ' '),
join(fromJSON(steps.colored_icons_detailed_img_step.outputs.markdown_urls), ' '),
steps.err_message_reader.outputs.content
)
}}