Skip to content

Commit b609cdc

Browse files
ci: allow multi submission
1 parent ca3e0c7 commit b609cdc

8 files changed

Lines changed: 133 additions & 118 deletions

File tree

.github/scripts/build_assets/arg_getters.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
from build_assets.PathResolverAction import PathResolverAction
33

44

5-
def get_selenium_runner_args(has_token=True, peek_mode=False):
5+
def get_selenium_runner_args(has_token=True):
66
"""
7-
Get the commandline arguments for the icomoon_peek.py and
7+
Get the commandline arguments for the icomoon_peek.py and
88
icomoon_build.py.
99
"""
1010
parser = ArgumentParser(description="Upload svgs to Icomoon to create icon files.")
@@ -33,13 +33,14 @@ def get_selenium_runner_args(has_token=True, peek_mode=False):
3333
help="The download destination of the Icomoon files",
3434
action=PathResolverAction)
3535

36-
if peek_mode:
37-
parser.add_argument("pr_title",
38-
help="The title of the PR that we are peeking at")
3936
if has_token != False:
4037
parser.add_argument("token",
4138
help="The GitHub token to access the GitHub REST API.")
4239

40+
parser.add_argument("changed_files",
41+
help="List of SVG files changed since the last release/tag",
42+
nargs="+")
43+
4344
return parser.parse_args()
4445

4546

@@ -49,9 +50,6 @@ def get_check_icon_pr_args():
4950
"""
5051
parser = ArgumentParser(description="Check the SVGs to ensure their attributes are correct. Run whenever a PR is opened")
5152

52-
parser.add_argument("pr_title",
53-
help="The title of the PR that we are peeking at")
54-
5553
parser.add_argument("icons_folder_path",
5654
help="The path to the icons folder",
5755
action=PathResolverAction)
@@ -60,6 +58,10 @@ def get_check_icon_pr_args():
6058
help="The path to the devicon.json",
6159
action=PathResolverAction)
6260

61+
parser.add_argument("changed_files",
62+
help="List of SVG files changed in the PR",
63+
nargs="+")
64+
6365
return parser.parse_args()
6466

6567

.github/scripts/build_assets/util.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,29 +50,26 @@ def set_env_var(key: str, value: str, delimiter: str='~'):
5050
raise Exception("This function doesn't support this platform: " + platform.system())
5151

5252

53-
def find_object_added_in_pr(icons: List[dict], pr_title: str):
53+
def find_changed_icons(icons: List[dict], changed_files: List[str]) -> List[dict]:
5454
"""
55-
Find the icon name from the PR title.
55+
Find the changed icons provided in the changed_files list.
5656
:param icons, a list of the font objects found in the devicon.json.
57-
:pr_title, the title of the PR that this workflow was called on.
58-
:return a dictionary with the "name"
59-
entry's value matching the name in the pr_title.
60-
:raise If no object can be found, raise an Exception.
57+
:param changed_files, SVG files changed in the PR or since the last release/tag.
58+
:return a list of dictionaries with the "name"
59+
entry values matching the name of changed icons.
6160
"""
62-
try:
63-
pattern = re.compile(r"(?<=^new icon: )\w+ (?=\(.+\))|(?<=^update icon: )\w+ (?=\(.+\))", re.I)
64-
icon_name_index = 0
65-
icon_name = pattern.findall(pr_title)[icon_name_index].lower().strip() # should only have one match
66-
icon = [icon for icon in icons if icon["name"] == icon_name][0]
67-
return icon
68-
except IndexError as e: # there are no match in the findall()
69-
print(e)
70-
message = "util.find_object_added_in_pr: Couldn't find an icon matching the name in the PR title.\n" \
71-
f"PR title is: '{pr_title}'"
72-
raise Exception(message)
61+
filtered_icons = []
62+
icon_names = []
63+
for file in changed_files:
64+
icon_name = Path(file).parent.name
65+
icon = [icon for icon in icons if icon["name"] == icon_name]
66+
if len(icon) > 0 and icon_name not in icon_names:
67+
icon_names.append(icon_name)
68+
filtered_icons.extend(icon)
69+
return filtered_icons
7370

7471

75-
def is_svg_in_font_attribute(svg_file_path: Path, devicon_object: dict):
72+
def is_svg_in_font_attribute(svg_file_path: Path, devicon_object: dict):
7673
"""
7774
Check if svg is in devicon.json's font attribute.
7875
:param svg_file_path, the path to a single svg icon

.github/scripts/check_icon_pr.py

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,45 @@ def main():
1818
try:
1919
all_icons = filehandler.get_json_file_content(args.devicon_json_path)
2020

21-
devicon_err_msg = []
22-
#First check if devicon.json is sorted
21+
err_msg = []
22+
# First check if devicon.json is sorted
2323
if sorted(all_icons, key=lambda d: d['name']) != all_icons:
24-
devicon_err_msg.append(f"devicon.json is not sorted correctly.\nPlease make sure that your icon is added in the `devicon.json` file at the correct alphabetic position\nas seen here: https://github.com/devicons/devicon/wiki/Updating-%60devicon.json%60")
24+
err_msg.append("devicon.json is not sorted correctly.\nPlease make sure that your icon is added in the `devicon.json` file at the correct alphabetic position\nas seen here: https://github.com/devicons/devicon/wiki/Updating-%60devicon.json%60")
2525

2626
# get only the icon object that has the name matching the pr title
27-
filtered_icon = util.find_object_added_in_pr(all_icons, args.pr_title)
28-
print("Checking devicon.json object: " + str(filtered_icon))
29-
devicon_err_msg.append(check_devicon_object(filtered_icon))
30-
31-
# check the file names
32-
filename_err_msg = ""
33-
svgs = None
34-
try:
35-
svgs = filehandler.get_svgs_paths([filtered_icon], args.icons_folder_path, as_str=False)
36-
print("SVGs to check: ", *svgs, sep='\n')
37-
except ValueError as e:
38-
filename_err_msg = "Error found regarding filenames:\n- " + e.args[0]
39-
40-
# check the svgs
41-
if svgs is None or len(svgs) == 0:
42-
print("No SVGs to check, ending script.")
43-
svg_err_msg = "Error checking SVGs: no SVGs to check. Might be caused by above issues."
44-
else:
45-
svg_err_msg = check_svgs(svgs, filtered_icon)
46-
47-
err_msg = []
48-
if devicon_err_msg != []:
49-
err_msg.extend(devicon_err_msg)
50-
51-
if filename_err_msg != "":
52-
err_msg.append(filename_err_msg)
53-
54-
if svg_err_msg != "":
55-
err_msg.append(svg_err_msg)
56-
27+
filtered_icons = util.find_changed_icons(all_icons, args.changed_files)
28+
for filtered_icon in filtered_icons:
29+
devicon_err_msg = []
30+
print("Checking devicon.json object: " + str(filtered_icon))
31+
devicon_err_msg.append(check_devicon_object(filtered_icon))
32+
33+
# check the file names
34+
filename_err_msg = ""
35+
svgs = None
36+
try:
37+
svgs = filehandler.get_svgs_paths([filtered_icon], args.icons_folder_path, as_str=False)
38+
print("SVGs to check: ", *svgs, sep='\n')
39+
except ValueError as e:
40+
filename_err_msg = "Error found regarding filenames:\n- " + e.args[0]
41+
42+
# check the svgs
43+
if svgs is None or len(svgs) == 0:
44+
print("No SVGs to check for this icon.")
45+
svg_err_msg = "Error checking SVGs: no SVGs to check. Might be caused by above issues."
46+
else:
47+
svg_err_msg = check_svgs(svgs, filtered_icon)
48+
49+
if devicon_err_msg:
50+
err_msg.extend(devicon_err_msg)
51+
52+
if filename_err_msg:
53+
err_msg.append(filename_err_msg)
54+
55+
if svg_err_msg:
56+
err_msg.append(svg_err_msg)
57+
58+
err_msg = list(filter(None, err_msg)) # remove empty strings from err_msg
59+
print("Error messages: ", err_msg)
5760
filehandler.write_to_file("./err_messages.txt", "\n\n".join(err_msg))
5861
print("Task completed.")
5962
except Exception as e:
@@ -109,7 +112,7 @@ def check_devicon_object(icon: dict):
109112
err_msgs.append(f"- Invalid version name in versions['svg']: '{version}'. Must match regexp: (original|plain|line)(-wordmark)?")
110113
except KeyError:
111114
err_msgs.append("- missing key: 'svg' in 'versions'.")
112-
115+
113116
try:
114117
if type(icon["versions"]["font"]) != list or len(icon["versions"]["svg"]) == 0:
115118
err_msgs.append("- must contain at least 1 font version in a list.")
@@ -160,7 +163,7 @@ def check_devicon_object(icon: dict):
160163
if len(err_msgs) > 0:
161164
message = "Error found in \"devicon.json\" for \"{}\" entry: \n{}".format(icon["name"], "\n".join(err_msgs))
162165
return message
163-
return ""
166+
return ""
164167

165168

166169
def check_svgs(svg_file_paths: List[Path], devicon_object: dict):

.github/scripts/icomoon_build.py

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ def main():
2222
logfile = open("log.txt", "w")
2323
try:
2424
args = arg_getters.get_selenium_runner_args()
25-
new_icons = get_icons_for_building(args.icomoon_json_path, args.devicon_json_path, args.token, logfile)
25+
new_icons = get_icons_for_building(args.devicon_json_path, args.changed_files)
2626
if len(new_icons) == 0:
2727
sys.exit("No files need to be uploaded. Ending script...")
2828

29-
print(f"There are {len(new_icons)} icons to be build. Here are they:", *new_icons, sep = "\n", file=logfile)
29+
print(f"There are {len(new_icons)} icons to be build. Here are they:", *new_icons, sep="\n", file=logfile)
3030

3131
print("Begin optimizing files...", file=logfile)
3232
optimize_svgs(new_icons, args.icons_folder_path, logfile=logfile)
@@ -39,7 +39,7 @@ def main():
3939
new_icons, args.icons_folder_path, icon_versions_only=True)
4040
zip_name = "devicon-v1.0.zip"
4141
zip_path = Path(args.download_path, zip_name)
42-
screenshot_folder = filehandler.create_screenshot_folder("./")
42+
screenshot_folder = filehandler.create_screenshot_folder("./")
4343

4444
runner = BuildSeleniumRunner(args.download_path,
4545
args.geckodriver_path, args.headless, log_output=logfile)
@@ -65,43 +65,24 @@ def main():
6565
finally:
6666
print("Exiting", file=logfile)
6767
if runner is not None:
68-
runner.close()
68+
runner.close()
6969
logfile.close()
7070

7171

72-
def get_icons_for_building(icomoon_json_path: str, devicon_json_path: str, token: str, logfile: FileIO):
72+
def get_icons_for_building(devicon_json_path: str, changed_files: List[str]) -> List[dict]:
7373
"""
7474
Get the icons for building.
75-
:param icomoon_json_path - the path to the `icomoon.json`.
7675
:param devicon_json_path - the path to the `devicon.json`.
77-
:param token - the token to access the GitHub API.
78-
:param logfile.
79-
:return a list of dict containing info on the icons. These are
76+
:param changed_files - the list of changed files since the last release/tag.
77+
78+
:return a list of dict containing info on the icons. These are
8079
from the `devicon.json`.
8180
"""
8281
devicon_json = filehandler.get_json_file_content(devicon_json_path)
83-
pull_reqs = api_handler.get_merged_pull_reqs_since_last_release(token, logfile)
84-
new_icons = []
85-
86-
for pull_req in pull_reqs:
87-
if api_handler.is_feature_icon(pull_req):
88-
filtered_icon = util.find_object_added_in_pr(devicon_json, pull_req["title"])
89-
if filtered_icon not in new_icons:
90-
new_icons.append(filtered_icon)
91-
92-
# get any icons that might not have been found by the API
93-
# sometimes happen due to the PR being opened before the latest build release
94-
new_icons_from_devicon_json = filehandler.find_new_icons_in_devicon_json(
95-
devicon_json_path, icomoon_json_path)
82+
return util.find_changed_icons(devicon_json, changed_files)
9683

97-
for icon in new_icons_from_devicon_json:
98-
if icon not in new_icons:
99-
new_icons.append(icon)
10084

101-
return new_icons
102-
103-
104-
def optimize_svgs(new_icons: List[str], icons_folder_path: str, logfile: FileIO):
85+
def optimize_svgs(new_icons: List[dict], icons_folder_path: str, logfile: FileIO):
10586
"""
10687
Optimize the newly added svgs. This is done in batches
10788
since the command line has a limit on characters allowed.
@@ -118,7 +99,7 @@ def optimize_svgs(new_icons: List[str], icons_folder_path: str, logfile: FileIO)
11899
subprocess.run(["npm", "run", "optimize-svg", "--", f"--svgFiles={json.dumps(batch)}"], shell=True)
119100

120101

121-
def update_icomoon_json(new_icons: List[str], icomoon_json_path: str, logfile: FileIO):
102+
def update_icomoon_json(new_icons: List[dict], icomoon_json_path: str, logfile: FileIO):
122103
"""
123104
Update the `icomoon.json` if it contains any icons
124105
that needed to be updated. This will remove the icons
@@ -129,23 +110,23 @@ def update_icomoon_json(new_icons: List[str], icomoon_json_path: str, logfile: F
129110
cur_len = len(icomoon_json["icons"])
130111
messages = []
131112

132-
wrapper_function = lambda icomoon_icon : find_icomoon_icon_not_in_new_icons(
113+
wrapper_function = lambda icomoon_icon: find_icomoon_icon_not_in_new_icons(
133114
icomoon_icon, new_icons, messages)
134115
icons_to_keep = filter(wrapper_function, icomoon_json["icons"])
135116
icomoon_json["icons"] = list(icons_to_keep)
136117

137118
new_len = len(icomoon_json["icons"])
138119
print(f"Update completed. Removed {cur_len - new_len} icons:", *messages, sep='\n', file=logfile)
139120
filehandler.write_to_file(icomoon_json_path, json.dumps(icomoon_json))
140-
121+
141122

142123
def find_icomoon_icon_not_in_new_icons(icomoon_icon: Dict, new_icons: List, messages: List):
143124
"""
144125
Find all the icomoon icons that are not listed in the new icons.
145126
This also add logging for which icons were removed.
146127
:param icomoon_icon - a dict object from the icomoon.json's `icons` attribute.
147128
:param new_icons - a list of new icons. Each element is an object from the `devicon.json`.
148-
:param messages - an empty list where the function can attach logging on which
129+
:param messages - an empty list where the function can attach logging on which
149130
icon were removed.
150131
"""
151132
for new_icon in new_icons:
@@ -182,7 +163,7 @@ def get_release_message(token, logfile: FileIO):
182163
thankYou = "A huge thanks to all our maintainers and contributors for making this release possible!"
183164
iconTitle = f"**{len(newIcons)} New Icons**"
184165
featureTitle = f"**{len(features)} New Features**"
185-
finalString = "{0}\n\n {1}\n{2}\n\n {3}\n{4}".format(thankYou,
166+
finalString = "{0}\n\n {1}\n{2}\n\n {3}\n{4}".format(thankYou,
186167
iconTitle, "\n".join(newIcons),
187168
featureTitle, "\n".join(features))
188169

.github/scripts/icomoon_peek.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,31 @@
55
def main():
66
runner = None
77
try:
8-
args = arg_getters.get_selenium_runner_args(has_token=False, peek_mode=True)
8+
args = arg_getters.get_selenium_runner_args(has_token=False)
99
all_icons = filehandler.get_json_file_content(args.devicon_json_path)
1010

11-
# get only the icon object that has the name matching the pr title
12-
filtered_icon = util.find_object_added_in_pr(all_icons, args.pr_title)
13-
svgs = filehandler.get_svgs_paths([filtered_icon], args.icons_folder_path, True)
14-
screenshot_folder = filehandler.create_screenshot_folder("./")
11+
# get only the icons that were changed in this PR
12+
filtered_icons = util.find_changed_icons(all_icons, args.changed_files)
13+
svgs = filehandler.get_svgs_paths(filtered_icons, args.icons_folder_path, True)
14+
screenshot_folder = filehandler.create_screenshot_folder("./")
1515

1616
runner = PeekSeleniumRunner(args.download_path, args.geckodriver_path, args.headless)
17-
svgs_with_strokes = runner.peek(svgs, screenshot_folder, filtered_icon)
18-
print("Task completed.")
19-
2017
message = ""
21-
if svgs_with_strokes != []:
22-
svgs_str = "\n\n".join(svgs_with_strokes)
23-
message = "\n### WARNING -- Strokes detected in the following SVGs:\n" + svgs_str + "\n"
18+
for filtered_icon in filtered_icons:
19+
svgs_with_strokes = runner.peek(svgs, screenshot_folder, filtered_icon)
20+
print(f"Task completed for {filtered_icon}.")
21+
22+
if svgs_with_strokes:
23+
svgs_str = "\n\n".join(svgs_with_strokes)
24+
message += "\n### WARNING -- Strokes detected in the following SVGs:\n" + svgs_str + "\n"
25+
2426
filehandler.write_to_file("./err_messages.txt", message)
2527
except Exception as e:
2628
filehandler.write_to_file("./err_messages.txt", str(e))
2729
util.exit_with_err(e)
2830
finally:
2931
if runner is not None:
30-
runner.close()
32+
runner.close()
3133

3234

3335
if __name__ == "__main__":

.github/workflows/build_icons.yml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ jobs:
66
runs-on: ubuntu-latest
77
steps:
88
- uses: actions/checkout@v4
9+
with:
10+
fetch-depth: '0'
11+
fetch-tags: 'true'
12+
913
- uses: actions/setup-python@v5
1014
with:
1115
python-version: '3.10'
@@ -20,10 +24,28 @@ jobs:
2024
shell: bash
2125
env:
2226
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23-
run: >
24-
python ./.github/scripts/icomoon_build.py
25-
./.github/scripts/build_assets/geckodriver-v0.32.2-linux64/geckodriver ./icomoon.json
26-
./devicon.json ./icons ./ $GITHUB_TOKEN --headless
27+
run: |
28+
echo "Getting the latest tags"
29+
git fetch --tags
30+
LATEST_TAG=$(git describe --tags --abbrev=0)
31+
echo "Latest tag: $LATEST_TAG"
32+
SECOND_LATEST_TAG=$(git describe --tags --abbrev=0 $LATEST_TAG^)
33+
echo "Second latest tag: $SECOND_LATEST_TAG"
34+
35+
echo "Getting the changed icons"
36+
CHANGED_ICONS=$(git diff --name-only $SECOND_LATEST_TAG..$LATEST_TAG | grep -E 'icons/.*\.svg')
37+
echo "Changed icons: $CHANGED_ICONS"
38+
39+
echo "Building the icons"
40+
python ./.github/scripts/icomoon_build.py \
41+
./.github/scripts/build_assets/geckodriver-v0.32.2-linux64/geckodriver \
42+
./icomoon.json \
43+
./devicon.json \
44+
./icons \
45+
./ \
46+
$GITHUB_TOKEN \
47+
--headless \
48+
${CHANGED_ICONS}
2749
2850
- name: Upload geckodriver.log for debugging purposes
2951
uses: actions/upload-artifact@v4

0 commit comments

Comments
 (0)