11from pathlib import Path
22import sys
33from selenium .common .exceptions import TimeoutException
4+ import re
45import subprocess
56import json
7+ from typing import List , Dict
8+
69
710# pycharm complains that build_assets is an unresolved ref
811# don't worry about it, the script still runs
912from build_assets .SeleniumRunner import SeleniumRunner
10- from build_assets import filehandler , arg_getters
11- from build_assets import util
13+ from build_assets import filehandler , arg_getters , util , api_handler
1214
1315
1416def main ():
15- args = arg_getters .get_selenium_runner_args ()
16- new_icons = filehandler .find_new_icons (args .devicon_json_path , args .icomoon_json_path )
17- if len (new_icons ) == 0 :
18- sys .exit ("No files need to be uploaded. Ending script..." )
19-
20- # print list of new icons
21- print ("List of new icons:" , * new_icons , sep = "\n " )
22-
17+ """
18+ Build the icons using Icomoon. Also optimize the svgs.
19+ """
2320 runner = None
2421 try :
25- svgs = filehandler .get_svgs_paths (new_icons , args .icons_folder_path , icon_versions_only = False )
26- # optimizes the files
27- # do in each batch in case the command
28- # line complains there's too many characters
29- start = 0
30- step = 10
31- for i in range (start , len (svgs ), step ):
32- batch = svgs [i :i + step ]
33- subprocess .run (["npm" , "run" , "optimize-svg" , "--" , f"--svgFiles={ json .dumps (batch )} " ], shell = True )
22+ args = arg_getters .get_selenium_runner_args ()
23+ new_icons = get_icons_for_building (args .devicon_json_path , args .token )
24+ if len (new_icons ) == 0 :
25+ sys .exit ("No files need to be uploaded. Ending script..." )
26+
27+ print (f"There are { len (new_icons )} icons to be build. Here are they:" , * new_icons , sep = "\n " )
28+
29+ print ("Begin optimizing files" )
30+ optimize_svgs (new_icons , args .icons_folder_path )
31+
32+ print ("Updating the icomoon json" )
33+ update_icomoon_json (new_icons , args .icomoon_json_path )
3434
3535 icon_svgs = filehandler .get_svgs_paths (
3636 new_icons , args .icons_folder_path , icon_versions_only = True )
@@ -50,7 +50,79 @@ def main():
5050 except Exception as e :
5151 util .exit_with_err (e )
5252 finally :
53- runner .close ()
53+ if runner is not None :
54+ runner .close ()
55+
56+
57+ def get_icons_for_building (devicon_json_path : str , token : str ):
58+ """
59+ Get the icons for building.
60+ :param devicon_json_path - the path to the `devicon.json`.
61+ :param token - the token to access the GitHub API.
62+ """
63+ all_icons = filehandler .get_json_file_content (devicon_json_path )
64+ pull_reqs = api_handler .get_merged_pull_reqs_since_last_release (token )
65+ new_icons = []
66+
67+ for pull_req in pull_reqs :
68+ if api_handler .is_feature_icon (pull_req ):
69+ filtered_icon = util .find_object_added_in_this_pr (all_icons , pull_req ["title" ])
70+ new_icons .append (filtered_icon )
71+ return new_icons
72+
73+
74+ def optimize_svgs (new_icons : List [str ], icons_folder_path : str ):
75+ """
76+ Optimize the newly added svgs. This is done in batches
77+ since the command line has a limit on characters allowed.
78+ :param new_icons - the new icons that need to be optimized.
79+ :param icons_folder_path - the path to the /icons folder.
80+ """
81+ svgs = filehandler .get_svgs_paths (new_icons , icons_folder_path , icon_versions_only = False )
82+ start = 0
83+ step = 10
84+ for i in range (start , len (svgs ), step ):
85+ batch = svgs [i :i + step ]
86+ subprocess .run (["npm" , "run" , "optimize-svg" , "--" , f"--svgFiles={ json .dumps (batch )} " ], shell = True )
87+
88+
89+ def update_icomoon_json (new_icons : List [str ], icomoon_json_path : str ):
90+ """
91+ Update the `icomoon.json` if it contains any icons
92+ that needed to be updated. This will remove the icons
93+ from the `icomoon.json` so the build script will reupload
94+ it later.
95+ """
96+ icomoon_json = filehandler .get_json_file_content (icomoon_json_path )
97+ cur_len = len (icomoon_json ["icons" ])
98+ messages = []
99+
100+ wrapper_function = lambda icomoon_icon : find_icomoon_icon_not_in_new_icons (
101+ icomoon_icon , new_icons , messages )
102+ icons_to_keep = filter (wrapper_function , icomoon_json ["icons" ])
103+ icomoon_json ["icons" ] = list (icons_to_keep )
104+
105+ new_len = len (icomoon_json ["icons" ])
106+ print (f"Update completed. Removed { cur_len - new_len } icons:" , * messages , sep = '\n ' )
107+ filehandler .write_to_file (icomoon_json_path , json .dumps (icomoon_json ))
108+
109+
110+ def find_icomoon_icon_not_in_new_icons (icomoon_icon : Dict , new_icons : List , messages : List ):
111+ """
112+ Find all the icomoon icons that are not listed in the new icons.
113+ This also add logging for which icons were removed.
114+ :param icomoon_icon - a dict object from the icomoon.json's `icons` attribute.
115+ :param new_icons - a list of new icons. Each element is an object from the `devicon.json`.
116+ :param messages - an empty list where the function can attach logging on which
117+ icon were removed.
118+ """
119+ for new_icon in new_icons :
120+ pattern = re .compile (f"^{ new_icon ['name' ]} -" )
121+ if pattern .search (icomoon_icon ["properties" ]["name" ]):
122+ message = f"-'{ icomoon_icon ['properties' ]['name' ]} ' cause it matches '{ new_icon ['name' ]} '"
123+ messages .append (message )
124+ return False
125+ return True
54126
55127
56128if __name__ == "__main__" :
0 commit comments