Skip to content
Closed
61 changes: 34 additions & 27 deletions .github/scripts/build_assets/SeleniumRunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,13 @@ def upload_icomoon(self, icomoon_json_path: str):

print("JSON file uploaded.")

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

self.click_hamburger_input()

for svg in svgs:
for i in range(len(svgs)):
import_btn = self.driver.find_element_by_css_selector(
"li.file input[type=file]"
)
import_btn.send_keys(svg)
print(f"Uploaded {svg}")
import_btn.send_keys(svgs[i])
print(f"Uploaded {svgs[i]}")
self.test_for_possible_alert(self.SHORT_WAIT_IN_SEC, "Dismiss")
self.remove_color_from_icon()
self.click_on_just_added_icon(screenshot_folder, i)

# take a screenshot of the icons that were just added
self.driver.save_screenshot("new_icons.png");
new_icons_path = str(Path(screenshot_folder, "new_icons.png").resolve())
self.driver.save_screenshot(new_icons_path);

# select all the svgs so that the newly added svg are part of the collection
self.click_hamburger_input()
select_all_button = WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
ec.element_to_be_clickable((By.XPATH, "//button[text()='Select All']"))
Expand Down Expand Up @@ -191,45 +197,46 @@ def test_for_possible_alert(self, wait_period: float, btn_text: str):
)
dismiss_btn.click()
except SeleniumTimeoutException:
pass
pass # nothing found => everything is good

def remove_color_from_icon(self):
def click_on_just_added_icon(self, screenshot_folder: str, index: int):
"""
Remove the color from the most recent uploaded icon.
:return: None.
Click on the most recently added icon so we can remove the colors
and take a snapshot if needed.
"""
try:
recently_uploaded_icon = WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
ec.element_to_be_clickable((By.XPATH, "//div[@id='set0']//mi-box[1]//div"))
)
recently_uploaded_icon.click()
except Exception as e:
self.close()
raise e

try:
color_tab = WebDriverWait(self.driver, self.SHORT_WAIT_IN_SEC).until(
ec.element_to_be_clickable((By.CSS_SELECTOR, "div.overlayWindow i.icon-droplet"))
)
color_tab.click()
self.remove_color_from_icon()

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

try:
close_btn = self.driver \
.find_element_by_css_selector("div.overlayWindow i.icon-close")
close_btn.click()
except Exception as e:
self.close()
raise e

def remove_color_from_icon(self):
"""
Remove the color from the most recent uploaded icon.
"""
color_tab = WebDriverWait(self.driver, self.SHORT_WAIT_IN_SEC).until(
ec.element_to_be_clickable((By.CSS_SELECTOR, "div.overlayWindow i.icon-droplet"))
)
color_tab.click()

remove_color_btn = self.driver \
.find_element_by_css_selector("div.overlayWindow i.icon-droplet-cross")
remove_color_btn.click()

def download_icomoon_fonts(self, zip_path: Path):
"""
Download the icomoon.zip from icomoon.io.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from pathlib import Path
from argparse import ArgumentParser
from build_assets.PathResolverAction import PathResolverAction

def get_commandline_args():

def get_selenium_runner_args(peek_mode=False):
parser = ArgumentParser(description="Upload svgs to Icomoon to create icon files.")

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

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

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

return parser.parse_args()
21 changes: 21 additions & 0 deletions .github/scripts/build_assets/filehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,24 @@ def rename_extracted_files(extract_path: str):
os.replace(dict_["old"], dict_["new"])

print("Files renamed")


def create_screenshot_folder(dir, screenshot_name: str="screenshots/"):
"""
Create a screenshots folder in the dir.
:param dir, the dir where we want to create the folder.
:param screenshot_name, the name of the screenshot folder.
:raise Exception if the dir provided is not a directory.
:return the string name of the screenshot folder.
"""
folder = Path(dir).resolve()
if not folder.is_dir():
raise Exception(f"This is not a dir: {str(folder)}. \ndir must be a valid directory")

screenshot_folder = Path(folder, screenshot_name)
try:
os.mkdir(screenshot_folder)
except FileExistsError:
print(f"{screenshot_folder} already exist. Script will do nothing.")
finally:
return str(screenshot_folder)
10 changes: 10 additions & 0 deletions .github/scripts/generate_screenshot_markdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import json
import os


if __name__ == "__main__":
img_urls_list = json.loads(os.environ["IMG_URLS"])
template = "![Detailed Screenshot]({})"
markdown = [template.format(img_url) for img_url in img_urls_list]
print("\n\n".join(markdown))

4 changes: 2 additions & 2 deletions .github/scripts/icomoon_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
# pycharm complains that build_assets is an unresolved ref
# don't worry about it, the script still runs
from build_assets.SeleniumRunner import SeleniumRunner
from build_assets import filehandler, util
from build_assets import filehandler, arg_getters


def main():
args = util.get_commandline_args()
args = arg_getters.get_selenium_runner_args()
new_icons = filehandler.find_new_icons(args.devicon_json_path, args.icomoon_json_path)
if len(new_icons) == 0:
print("No files need to be uploaded. Ending script...")
Expand Down
44 changes: 37 additions & 7 deletions .github/scripts/icomoon_peek.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,63 @@
from typing import List
import re
from selenium.common.exceptions import TimeoutException

# pycharm complains that build_assets is an unresolved ref
# don't worry about it, the script still runs
from build_assets.SeleniumRunner import SeleniumRunner
from build_assets import filehandler, util
from build_assets import filehandler, arg_getters


def main():
args = util.get_commandline_args()
args = arg_getters.get_selenium_runner_args(True)
new_icons = filehandler.find_new_icons(args.devicon_json_path, args.icomoon_json_path)

# get only the icon object that has the name matching the pr title
filtered_icons = find_object_added_in_this_pr(new_icons, args.pr_title)

# print list of new icons
print("List of new icons:", *new_icons, sep = "\n")
print("Icons being uploaded:", *filtered_icons, sep = "\n")

if len(new_icons) == 0:
print("No files need to be uploaded. Ending script...")
return

# print list of new icons
print("List of new icons:", *new_icons, sep = "\n")
screenshot_folder = filehandler.create_screenshot_folder("./")
if len(filtered_icons) == 0:
print("No icons found matching the icon name in the PR's title. Fallback to uploading all new icons found.")
screenshot_folder = ""

runner = None
try:
runner = SeleniumRunner(args.download_path, args.geckodriver_path, args.headless)
svgs = filehandler.get_svgs_paths(new_icons, args.icons_folder_path)
runner.upload_svgs(svgs)
svgs = filehandler.get_svgs_paths(filtered_icons, args.icons_folder_path)
runner.upload_svgs(svgs, screenshot_folder)
print("Task completed.")
except TimeoutException as e:
print("Selenium Time Out Error: ", e.stacktrace, sep='\n')
except Exception as e:
print(e)
print(e.stacktrace)
finally:
runner.close()


def find_object_added_in_this_pr(icons: List[dict], pr_title: str):
"""
Find the icon name from the PR title.
:param icons, a list of the font objects found in the devicon.json.
:pr_title, the title of the PR that this workflow was called on.
:return a list containing the dictionary with the "name"
entry's value matching the name in the pr_title.
If none can be found, return an empty list.
"""
try:
pattern = re.compile(r"(?<=^new icon: )\w+ (?=\(.+\))", re.I)
icon_name = pattern.findall(pr_title)[0].lower().strip() # should only have one match
return [icon for icon in icons if icon["name"] == icon_name]
except IndexError: # there are no match in the findall()
return []


if __name__ == "__main__":
main()
33 changes: 23 additions & 10 deletions .github/workflows/build_icons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,42 @@ jobs:
pip install -r ./.github/scripts/requirements.txt
npm install
- name: Executing build and create fonts via icomoon
run: npm run build
run: >
python ./.github/scripts/icomoon_build.py
./.github/scripts/build_assets/geckodriver-v0.27.0-win64/geckodriver.exe ./icomoon.json
./devicon.json ./icons ./ --headless
- name: Upload geckodriver.log for debugging purposes
uses: actions/upload-artifact@v2
if: ${{failure()}}
if: failure()
with:
name: geckodriver-log
path: ./geckodriver.log
- name: Build devicon.min.css
if: success()
run: npm run build-css
- name: Upload screenshot of the newly made icons
uses: actions/upload-artifact@v2
if: ${{success()}}
id: imgur_step
uses: devicons/public-upload-to-imgur@v2
if: success()
with:
name: new_icons
path: ./new_icons.png
- name: Build devicon.min.css
if: ${{ success() }}
run: npm run build-css
client_id: ${{secrets.IMGUR_CLIENT_ID}}
- name: Create Pull Request
if: ${{ success() }}
if: success()
uses: peter-evans/create-pull-request@v3
env:
MESSAGE: |
Automated font-building task ran by GitHub Actions bot. This PR built new font files and devicon.css file.

Here are all the files that were built:

![Files Built]({0})

More information can be found in the GitHub Action logs for this workflow.
with:
branch: 'master-build-result'
base: 'master'
commit-message: 'Built new icons, icomoon.json and devicon.css'
title: 'bot:build new icons, icomoon.json and devicon.css'
body: 'Automated font-building task ran by GitHub Actions bot'
body: ${{ format(env.MESSAGE, steps.imgur_step.outputs.imgur_url ) }}
delete-branch: true
72 changes: 53 additions & 19 deletions .github/workflows/peek_icons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,69 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies (python, pip)
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r ./.github/scripts/requirements.txt
- name: Run icomoon_peek.py
run: npm run peek
env:
PR_TITLE: ${{ github.event.pull_request.title }}
shell: cmd
run: >
python ./.github/scripts/icomoon_peek.py
./.github/scripts/build_assets/geckodriver-v0.27.0-win64/geckodriver.exe ./icomoon.json
./devicon.json ./icons ./ --headless --pr_title "%PR_TITLE%"
- name: Upload geckodriver.log for debugging purposes
uses: actions/upload-artifact@v2
if: ${{failure()}}
if: failure()
with:
name: geckodriver-log
path: ./geckodriver.log
- name: Upload screenshot of the newly made icons
uses: actions/upload-artifact@v2
if: ${{success()}}
id: icons_overview_img_step
uses: devicons/public-upload-to-imgur@v2
if: success()
with:
path: ./screenshots/new_icons.png
client_id: ${{secrets.IMGUR_CLIENT_ID}}
- name: Upload zoomed in screenshot of the newly made icons
id: icons_detailed_img_step
uses: devicons/public-upload-to-imgur@v2
if: success()
with:
name: new_icons
path: ./new_icons.png
# - name: Comment on the PR about the result
# uses: github-actions-up-and-running/pr-comment@v1.0.1
# with:
# repo-token: ${{ secrets.GITHUB_TOKEN }}
# message: >
# Hi! I'm Devicons' GitHub Actions Bot!

# I just peeked at the icons that you wanted to add and upload them to the
# [Actions page](https://github.com/devicons/devicon/actions). The maintainers
# will now take a look at it and decide whether to merge your PR.
path: ./screenshots/screenshot_*.png
client_id: ${{secrets.IMGUR_CLIENT_ID}}
- name: Generate the markdowns for the screenshot and put it in the DETAILED_IMGS_MARKDOWN env var
if: success()
env:
IMG_URLS: ${{ steps.icons_detailed_img_step.outputs.imgur_urls }}
run: |
echo 'DETAILED_IMGS_MARKDOWN<<EOF' >> $GITHUB_ENV
python ./.github/scripts/generate_screenshot_markdown.py >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
shell: bash
- name: Comment on the PR about the result
uses: github-actions-up-and-running/pr-comment@v1.0.1
env:
OVERVIEW_IMG_URL: ${{ fromJSON(steps.icons_overview_img_step.outputs.imgur_urls)[0] }}
MESSAGE: |
Hi!

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).
Here is the result below:

![Peeked Icons (top left)]({0})

# Cheers :),
Here are the zoomed-in screenshots of the added icons:
{1}

Note: If the images doesn't show up, it's probably because it has been autodeleted by Imgur after 6 months due to our API choice.

The maintainers will now take a look at it and decide whether to merge your PR.

Cheers :),

# Bot
Peek Bot
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
message: ${{format(env.MESSAGE, env.OVERVIEW_IMG_URL, env.DETAILED_IMGS_MARKDOWN)}}
Loading