mirror of
https://github.com/konpa/devicon.git
synced 2025-09-02 02:42:48 +02:00
Script upgrades and updated CONTRIBUTING.md and README.md (#576)
* Updated README and CONTRIBUTING * Added check for devicon object when peeking * Added PR template * Added a script to create release messages * Updated CONTRIBUTING about new script * Update .github/PULL_REQUEST_TEMPLATE/new_icon.md Co-authored-by: David Leal <halfpacho@gmail.com> * Update .github/scripts/build_assets/arg_getters.py Co-authored-by: David Leal <halfpacho@gmail.com> * Update .github/workflows/get_release_message.yml Co-authored-by: David Leal <halfpacho@gmail.com> * Update gulpfile.js Co-authored-by: David Leal <halfpacho@gmail.com> * Update .github/PULL_REQUEST_TEMPLATE/new_feature.md Co-authored-by: David Leal <halfpacho@gmail.com> * Update .github/PULL_REQUEST_TEMPLATE/new_feature.md Co-authored-by: David Leal <halfpacho@gmail.com> * Added a way for peek bot to comment error * Update CONTRIBUTING.md Co-authored-by: Clemens Bastian <8781699+amacado@users.noreply.github.com> * Update .github/scripts/get_release_message.py Co-authored-by: Malte Jürgens <maltejur@web.de> * Update .github/scripts/get_release_message.py Co-authored-by: Malte Jürgens <maltejur@web.de> * Update .github/PULL_REQUEST_TEMPLATE/new_feature.md Co-authored-by: David Leal <halfpacho@gmail.com> * Clean up and updated CONTRIBUTING * Updated CONTRIBUTING * Add set up steps for release message workflow * Refactored peek workflow * Added requests library * Reformat devicon object error messages Co-authored-by: David Leal <halfpacho@gmail.com> Co-authored-by: Clemens Bastian <8781699+amacado@users.noreply.github.com> Co-authored-by: Malte Jürgens <maltejur@web.de>
This commit is contained in:
9
.github/PULL_REQUEST_TEMPLATE/new_feature.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE/new_feature.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
## This PR adds...
|
||||
|
||||
*List your features here and their reasons for creation.*
|
||||
|
||||
## Notes
|
||||
|
||||
*List anything note-worthy here (potential issues, this needs merge to `master` before working, etc....).*
|
||||
|
||||
*Don't forget to link any issues that this PR will solved.*
|
9
.github/PULL_REQUEST_TEMPLATE/new_icon.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE/new_icon.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
**Double check these details before you open a PR**
|
||||
|
||||
- [] PR does not match another non-stale PR currently opened
|
||||
- [] PR name matches the format *new icon: <i>Icon name</i> (<i>versions separated by comma</i>)* as seen [here](https://github.com/devicons/devicon/blob/develop/CONTRIBUTING.md#overview)
|
||||
- [] Your icons are put in a folder as seen [here](https://github.com/devicons/devicon/blob/develop/CONTRIBUTING.md#organizational-guidelines)
|
||||
- [] SVG matches the standards laid out [here](https://github.com/devicons/devicon/blob/develop/CONTRIBUTING.md#svgStandards)
|
||||
- [] A new object is added in the `devicon.json` file as seen [here](https://github.com/devicons/devicon/blob/develop/CONTRIBUTING.md#-updating-the-deviconjson-)
|
||||
|
||||
Refer to the [`CONTRIBUTING.md`](https://github.com/devicons/devicon/blob/develop/CONTRIBUTING.md#contributing-to-devicon) for more details.
|
37
.github/drafts/check_devicon_object.py
vendored
37
.github/drafts/check_devicon_object.py
vendored
@@ -1,37 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
# abandoned since it's not too hard to check devicon objects using our eyes
|
||||
# however, still keep in case we need it in the future
|
||||
|
||||
def check_devicon_objects(icons: List[dict]):
|
||||
"""
|
||||
Check that the devicon objects added is up to standard.
|
||||
"""
|
||||
err_msgs = []
|
||||
for icon in icons:
|
||||
if type(icon["name"]) != str:
|
||||
err_msgs.append("'name' must be a string, not: " + str(icon["name"]))
|
||||
|
||||
try:
|
||||
for tag in icon["tags"]:
|
||||
if type(tag) != str:
|
||||
raise TypeError()
|
||||
except TypeError:
|
||||
err_msgs.append("'tags' must be an array of strings, not: " + str(icon["tags"]))
|
||||
break
|
||||
|
||||
|
||||
if type(icon["versions"]["svg"]) != list or len(icon["versions"]["svg"]) == 0:
|
||||
err_msgs.append("Icon name must be a string")
|
||||
|
||||
if type(icon["versions"]["font"]) != list or len(icon["versions"]["svg"]) == 0:
|
||||
err_msgs.append("Icon name must be a string")
|
||||
|
||||
if type(icon["color"]) != str or "#" not in icon["color"]:
|
||||
err_msgs.append("'color' must be a string in the format '#abcdef'")
|
||||
|
||||
if type(icon["aliases"]) != list:
|
||||
err_msgs.append("'aliases' must be an array of dicts")
|
||||
|
||||
if len(err_msgs) > 0:
|
||||
raise Exception("Error found in devicon.json: \n" + "\n".join(err_msgs))
|
13
.github/scripts/build_assets/arg_getters.py
vendored
13
.github/scripts/build_assets/arg_getters.py
vendored
@@ -67,4 +67,15 @@ def get_check_svgs_monthly_args():
|
||||
parser.add_argument("icons_folder_path",
|
||||
help="The path to the icons folder",
|
||||
action=PathResolverAction)
|
||||
return parser.parse_args()
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def get_release_message_args():
|
||||
"""
|
||||
Get the commandline arguments for get_release_message.py.
|
||||
"""
|
||||
parser = ArgumentParser(description="Create a text containing the icons and features added since last release.")
|
||||
parser.add_argument("token",
|
||||
help="The GitHub token to access the GitHub REST API.",
|
||||
type=str)
|
||||
return parser.parse_args()
|
||||
|
75
.github/scripts/get_release_message.py
vendored
Normal file
75
.github/scripts/get_release_message.py
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
import requests
|
||||
from build_assets import arg_getters
|
||||
import re
|
||||
|
||||
def main():
|
||||
print("Please wait a few seconds...")
|
||||
args = arg_getters.get_release_message_args()
|
||||
queryPath = "https://api.github.com/repos/devicons/devicon/pulls?accept=application/vnd.github.v3+json&state=closed&per_page=100"
|
||||
stopPattern = r"^(r|R)elease v"
|
||||
headers = {
|
||||
"Authorization": f"token {args.token}"
|
||||
}
|
||||
|
||||
response = requests.get(queryPath, headers=headers)
|
||||
if not response:
|
||||
print(f"Can't query the GitHub API. Status code is {response.status_code}. Message is {response.text}")
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
newIcons = []
|
||||
features = []
|
||||
|
||||
for pullData in data:
|
||||
if re.search(stopPattern, pullData["title"]):
|
||||
break
|
||||
|
||||
authors = findAllAuthors(pullData, headers)
|
||||
markdown = f"- [{pullData['title']}]({pullData['html_url']}) by {authors}."
|
||||
|
||||
if isFeatureIcon(pullData):
|
||||
newIcons.append(markdown)
|
||||
else:
|
||||
features.append(markdown)
|
||||
|
||||
thankYou = "A huge thanks to all our maintainers and contributors for making this release possible!"
|
||||
iconTitle = "**{} New Icons**\n".format(len(newIcons))
|
||||
featureTitle = "**{} New Features**\n".format(len(features))
|
||||
finalString = "{0}\n\n {1}{2}\n\n {3}{4}".format(thankYou,
|
||||
iconTitle, "\n".join(newIcons), featureTitle, "\n".join(features))
|
||||
|
||||
print("--------------Here is the build message--------------\n", finalString)
|
||||
|
||||
|
||||
"""
|
||||
Check whether the pullData is a feature:icon PR.
|
||||
:param pullData
|
||||
:return true if the pullData has a label named "feature:icon"
|
||||
"""
|
||||
def isFeatureIcon(pullData):
|
||||
for label in pullData["labels"]:
|
||||
if label["name"] == "feature:icon":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
"""
|
||||
Find all the authors of a PR based on its commits.
|
||||
:param pullData - the data of a pull request.
|
||||
"""
|
||||
def findAllAuthors(pullData, authHeader):
|
||||
response = requests.get(pullData["commits_url"], headers=authHeader)
|
||||
if not response:
|
||||
print(f"Can't query the GitHub API. Status code is {response.status_code}")
|
||||
print("Response is: ", response.text)
|
||||
return
|
||||
|
||||
commits = response.json()
|
||||
authors = set() # want unique authors only
|
||||
for commit in commits:
|
||||
authors.add("@" + commit["author"]["login"])
|
||||
return ", ".join(list(authors))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
105
.github/scripts/icomoon_peek.py
vendored
105
.github/scripts/icomoon_peek.py
vendored
@@ -2,7 +2,6 @@ from typing import List
|
||||
import re
|
||||
import sys
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
# pycharm complains that build_assets is an unresolved ref
|
||||
# don't worry about it, the script still runs
|
||||
@@ -12,36 +11,28 @@ from build_assets import util
|
||||
|
||||
|
||||
def main():
|
||||
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)
|
||||
|
||||
if len(new_icons) == 0:
|
||||
sys.exit("No files need to be uploaded. Ending script...")
|
||||
|
||||
if len(filtered_icons) == 0:
|
||||
message = "No icons found matching the icon name in the PR's title.\n" \
|
||||
"Ensure that a new icon entry is added in the devicon.json and the PR title matches the convention here: \n" \
|
||||
"https://github.com/devicons/devicon/blob/master/CONTRIBUTING.md#overview\n" \
|
||||
"Ending script...\n"
|
||||
sys.exit(message)
|
||||
|
||||
# print list of new icons
|
||||
print("List of new icons:", *new_icons, sep = "\n")
|
||||
print("Icons being uploaded:", *filtered_icons, sep = "\n", end='\n\n')
|
||||
|
||||
runner = None
|
||||
try:
|
||||
args = arg_getters.get_selenium_runner_args(True)
|
||||
new_icons = filehandler.find_new_icons(args.devicon_json_path, args.icomoon_json_path)
|
||||
|
||||
if len(new_icons) == 0:
|
||||
raise Exception("No files need to be uploaded. Ending script...")
|
||||
|
||||
# get only the icon object that has the name matching the pr title
|
||||
filtered_icon = find_object_added_in_this_pr(new_icons, args.pr_title)
|
||||
print("Icon being checked:", filtered_icon, sep = "\n", end='\n\n')
|
||||
|
||||
runner = SeleniumRunner(args.download_path, args.geckodriver_path, args.headless)
|
||||
svgs = filehandler.get_svgs_paths(filtered_icons, args.icons_folder_path, True)
|
||||
svgs = filehandler.get_svgs_paths([filtered_icon], args.icons_folder_path, True)
|
||||
screenshot_folder = filehandler.create_screenshot_folder("./")
|
||||
runner.upload_svgs(svgs, screenshot_folder)
|
||||
print("Task completed.")
|
||||
except TimeoutException as e:
|
||||
util.exit_with_err("Selenium Time Out Error: \n" + str(e))
|
||||
|
||||
# no errors, do this so upload-artifact won't fail
|
||||
filehandler.write_to_file("./err_messages.txt", "0")
|
||||
except Exception as e:
|
||||
filehandler.write_to_file("./err_messages.txt", str(e))
|
||||
util.exit_with_err(e)
|
||||
finally:
|
||||
runner.close()
|
||||
@@ -52,19 +43,77 @@ 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"
|
||||
:return a dictionary with the "name"
|
||||
entry's value matching the name in the pr_title.
|
||||
If none can be found, return an empty list.
|
||||
:raise If no object can be found, raise an Exception.
|
||||
"""
|
||||
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]
|
||||
icon = [icon for icon in icons if icon["name"] == icon_name][0]
|
||||
check_devicon_object(icon, icon_name)
|
||||
return icon
|
||||
except IndexError: # there are no match in the findall()
|
||||
return []
|
||||
raise Exception("Couldn't find an icon matching the name in the PR title.")
|
||||
except ValueError as e:
|
||||
raise Exception(str(e))
|
||||
|
||||
|
||||
def check_devicon_object(icon: dict, icon_name: str):
|
||||
"""
|
||||
Check that the devicon object added is up to standard.
|
||||
:return a string containing the error messages if any.
|
||||
"""
|
||||
err_msgs = []
|
||||
try:
|
||||
if icon["name"] != icon_name:
|
||||
err_msgs.append("- 'name' value is not: " + icon_name)
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'name'.")
|
||||
|
||||
try:
|
||||
for tag in icon["tags"]:
|
||||
if type(tag) != str:
|
||||
raise TypeError()
|
||||
except TypeError:
|
||||
err_msgs.append("- 'tags' must be an array of strings, not: " + str(icon["tags"]))
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'tags'.")
|
||||
|
||||
try:
|
||||
if type(icon["versions"]) != dict:
|
||||
err_msgs.append("- 'versions' must be an object.")
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'versions'.")
|
||||
|
||||
try:
|
||||
if type(icon["versions"]["svg"]) != list or len(icon["versions"]["svg"]) == 0:
|
||||
err_msgs.append("- must contain at least 1 svg version in a list.")
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'svg' in 'versions'.")
|
||||
|
||||
try:
|
||||
if type(icon["versions"]["font"]) != list or len(icon["versions"]["svg"]) == 0:
|
||||
err_msgs.append("- must contain at least 1 font version in a list.")
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'font' in 'versions'.")
|
||||
|
||||
try:
|
||||
if type(icon["color"]) != str or "#" not in icon["color"]:
|
||||
err_msgs.append("- 'color' must be a string in the format '#abcdef'")
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'color'.")
|
||||
|
||||
try:
|
||||
if type(icon["aliases"]) != list:
|
||||
err_msgs.append("- 'aliases' must be an array.")
|
||||
except KeyError:
|
||||
err_msgs.append("- missing key: 'aliases'.")
|
||||
|
||||
if len(err_msgs) > 0:
|
||||
message = "Error found in 'devicon.json' for '{}' entry: \n{}".format(icon_name, "\n".join(err_msgs))
|
||||
raise ValueError(message)
|
||||
return ""
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
3
.github/scripts/requirements.txt
vendored
3
.github/scripts/requirements.txt
vendored
@@ -1 +1,2 @@
|
||||
selenium==3.141.0
|
||||
selenium==3.141.0
|
||||
requests==2.25.1
|
23
.github/workflows/get_release_message.yml
vendored
Normal file
23
.github/workflows/get_release_message.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Get Release Message
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
build:
|
||||
name: Get Fonts From Icomoon
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Python v3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r ./.github/scripts/requirements.txt
|
||||
|
||||
- name: Run the get_release_message.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: python ./.github/scripts/get_release_message.py $GITHUB_TOKEN
|
7
.github/workflows/peek_icons.yml
vendored
7
.github/workflows/peek_icons.yml
vendored
@@ -44,6 +44,13 @@ jobs:
|
||||
./.github/scripts/build_assets/geckodriver-v0.27.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
|
||||
if: always()
|
||||
with:
|
||||
name: err_messages
|
||||
path: ./err_messages.txt
|
||||
|
||||
- name: Upload screenshots for comments
|
||||
uses: actions/upload-artifact@v2
|
||||
if: success()
|
||||
|
20
.github/workflows/post_peek_screenshot.yml
vendored
20
.github/workflows/post_peek_screenshot.yml
vendored
@@ -32,6 +32,14 @@ jobs:
|
||||
with:
|
||||
path: ./pr_num/pr_num.txt
|
||||
|
||||
- name: Read the err message file
|
||||
if: success()
|
||||
id: err_message_reader
|
||||
uses: juliangruber/read-file-action@v1.0.0
|
||||
with:
|
||||
path: ./err_messages/err_messages.txt
|
||||
|
||||
|
||||
- name: Upload screenshot of the newly made icons gotten from the artifacts
|
||||
id: icons_overview_img_step
|
||||
if: env.PEEK_STATUS == 'success' && success()
|
||||
@@ -87,15 +95,19 @@ jobs:
|
||||
MESSAGE: |
|
||||
Hi there,
|
||||
|
||||
I'm Devicons' Peek Bot and it seems we've ran into a problem (sorry!).
|
||||
I'm Devicons' Peek Bot and it seems we've ran into a problem.
|
||||
|
||||
Please double check and fix the possible issues below:
|
||||
```
|
||||
{0}
|
||||
```
|
||||
|
||||
Make sure that:
|
||||
|
||||
- Your svgs are named and added correctly to the /icons folder as seen [here](https://github.com/devicons/devicon/blob/master/CONTRIBUTING.md#orgGuidelines).
|
||||
- Your icon information has been added to the `devicon.json` as seen [here](https://github.com/devicons/devicon/blob/master/CONTRIBUTING.md#updateDevicon)
|
||||
- Your PR title follows the format seen [here](https://github.com/devicons/devicon/blob/master/CONTRIBUTING.md#overview)
|
||||
|
||||
I will retry once everything is fixed. If I still fail (sorry!) or there are other erros, the maintainers will investigate.
|
||||
I will retry once everything is fixed. If I still fail or there are other error, the maintainers will investigate.
|
||||
|
||||
Best of luck,
|
||||
Peek Bot :relaxed:
|
||||
@@ -103,4 +115,4 @@ jobs:
|
||||
type: create
|
||||
issue_number: ${{ steps.pr_num_reader.outputs.content }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: ${{ env.MESSAGE }}
|
||||
body: ${{ format(env.MESSAGE, steps.err_message_reader.outputs.content) }}
|
||||
|
Reference in New Issue
Block a user