4
4
import urllib .request
5
5
import hashlib
6
6
from pathlib import Path
7
- import subprocess
8
- from tqdm import tqdm
9
7
import shutil
10
8
import zipfile
11
9
from datetime import datetime
10
+ import warnings
11
+ import platform
12
+ from tqdm import tqdm
13
+ from tqdm .std import TqdmWarning
14
+ from colorama import Fore , Style
15
+ import argparse
16
+
17
+ warnings .filterwarnings ("ignore" , category = TqdmWarning )
12
18
13
19
def get_package_file_path ():
14
- if sys . platform == "win32 " :
20
+ if platform . system () == "Windows " :
15
21
return Path (os .getenv ('APPDATA' )) / "ravendevteam" / "toolbox" / "packages.json"
16
22
else :
17
23
return Path .home () / "Library" / "Application Support" / "ravendevteam" / "toolbox" / "packages.json"
18
24
19
25
def get_installation_path (package_name ):
20
- if sys . platform == "win32 " :
26
+ if platform . system () == "Windows " :
21
27
return Path (os .getenv ('APPDATA' )) / "ravendevteam" / package_name
22
28
else :
23
29
return Path .home () / "Library" / "Application Support" / "ravendevteam" / package_name
24
30
25
31
def handle_error (message ):
26
- print (f"Error: { message } " )
32
+ print (Fore . RED + f"Error: { message } " + Style . RESET_ALL )
27
33
sys .exit (1 )
28
34
35
+ def handle_warning (message ):
36
+ print (Fore .YELLOW + f"Warning: { message } " + Style .RESET_ALL )
37
+
29
38
def get_record_file_path ():
30
- if sys . platform == "win32 " :
39
+ if platform . system () == "Windows " :
31
40
return Path (os .getenv ('APPDATA' )) / "ravendevteam" / "record.json"
32
41
else :
33
42
return Path .home () / "Library" / "Application Support" / "ravendevteam" / "record.json"
@@ -48,17 +57,18 @@ def write_record(record):
48
57
with open (record_file , 'w' ) as f :
49
58
json .dump (record , f , indent = 4 )
50
59
51
- def uninstall_package (package_name ):
60
+ def uninstall_package (package_name , skip_confirmation ):
52
61
try :
53
62
install_path = get_installation_path (package_name )
54
63
if not install_path .exists ():
55
64
handle_error (f"Package '{ package_name } ' is not installed." )
56
- confirmation = input (f"Are you sure you want to uninstall '{ package_name } '? (Y/N): " ).strip ().lower ()
57
- if confirmation != 'y' :
58
- print (f"Uninstallation of '{ package_name } ' cancelled." )
59
- return
65
+ if not skip_confirmation :
66
+ confirmation = input (f"Are you sure you want to uninstall '{ package_name } '? (Y/N): " ).strip ().lower ()
67
+ if confirmation != 'y' :
68
+ print (Fore .WHITE + f"Uninstallation of '{ package_name } ' cancelled." + Style .RESET_ALL )
69
+ return
60
70
shutil .rmtree (install_path )
61
- print (f"'{ package_name } ' has been successfully uninstalled." )
71
+ print (Fore . GREEN + f"'{ package_name } ' has been successfully uninstalled." + Style . RESET_ALL )
62
72
record = read_record ()
63
73
if package_name in record :
64
74
del record [package_name ]
@@ -71,21 +81,14 @@ def update_packages():
71
81
try :
72
82
with open (get_package_file_path (), 'r' ) as f :
73
83
data = json .load (f )
74
- update_url = data .get ('updateurl' , None )
84
+ update_url = data .get ('updateurl' , default_update_url )
75
85
except (FileNotFoundError , json .JSONDecodeError ):
76
- update_url = None
77
- if not update_url :
78
86
update_url = default_update_url
79
- print (f"Warning: No update URL found or the package list is missing. Using default update URL: { default_update_url } " )
80
- print ("Please note that the above error is normal upon first time updating." )
81
- print ("Updating package list from server..." )
87
+ print (Fore .WHITE + f"Using default update URL: { default_update_url } " + Style .RESET_ALL )
88
+ print (Fore .WHITE + "Updating package list from server..." + Style .RESET_ALL )
82
89
try :
83
90
urllib .request .urlretrieve (update_url , get_package_file_path ())
84
- print ("Update successful!" )
85
- except urllib .error .HTTPError as e :
86
- handle_error (f"HTTP Error: { e .code } - { e .reason } . Please check the update URL or try again later." )
87
- except urllib .error .URLError as e :
88
- handle_error (f"URL Error: { str (e )} . Please check your internet connection or try again later." )
91
+ print (Fore .GREEN + "Update successful!" + Style .RESET_ALL )
89
92
except Exception as e :
90
93
handle_error (f"Failed to update packages: { str (e )} . Please try again or check your settings." )
91
94
@@ -115,115 +118,95 @@ def validate_checksum(file_path, expected_hash):
115
118
return sha256_hash .hexdigest () == expected_hash
116
119
117
120
def create_shortcut (target , shortcut_name ):
118
- if sys .platform == "win32" :
119
- from win32com .client import Dispatch
120
- shell = Dispatch ('WScript.Shell' )
121
- desktop = shell .SpecialFolders ('Desktop' )
122
- shortcut = shell .CreateShortCut (os .path .join (desktop , f"{ shortcut_name } .lnk" ))
123
- shortcut .TargetPath = target
124
- shortcut .WorkingDirectory = os .path .dirname (target )
125
- shortcut .IconLocation = target
126
- shortcut .save ()
127
- else :
128
- os .symlink (target , Path .home () / "Desktop" / f"{ shortcut_name } .app" )
121
+ try :
122
+ if platform .system () == "Windows" :
123
+ from win32com .client import Dispatch
124
+ shell = Dispatch ('WScript.Shell' )
125
+ desktop = shell .SpecialFolders ('Desktop' )
126
+ shortcut = shell .CreateShortCut (os .path .join (desktop , f"{ shortcut_name } .lnk" ))
127
+ shortcut .TargetPath = target
128
+ shortcut .WorkingDirectory = os .path .dirname (target )
129
+ shortcut .IconLocation = target
130
+ shortcut .save ()
131
+ else :
132
+ os .symlink (target , Path .home () / "Desktop" / f"{ shortcut_name } .app" )
133
+ except Exception :
134
+ handle_warning (f"Could not create a shortcut for '{ shortcut_name } '. This will not affect the installation." )
129
135
130
- def install_package (package_name ):
136
+ def install_package (package_name , skip_confirmation ):
131
137
try :
132
138
with open (get_package_file_path (), 'r' ) as f :
133
139
data = json .load (f )
134
- package = next ((pkg for pkg in data .get ("packages" , []) if pkg ["name" ].lower () == package_name .lower ()), None )
140
+ packages = data .get ("packages" , [])
141
+ if package_name == '*' :
142
+ for package in packages :
143
+ install_package (package ['name' ], skip_confirmation )
144
+ return
145
+ package = next ((pkg for pkg in packages if pkg ["name" ].lower () == package_name .lower ()), None )
135
146
if not package :
136
147
handle_error (f"Package '{ package_name } ' not found in the package list." )
137
- platform = "Windows" if sys .platform == "win32" else "macOS"
138
- if platform not in package ["os" ]:
139
- handle_error (f"'{ package_name } ' is not available for your platform ({ platform } )." )
140
- url = package ["url" ][platform ]
141
- sha256 = package ["sha256" ][platform ]
142
- print (f"Installing { package_name } (v{ package ['version' ]} ) for { platform } ..." )
148
+ platform_name = platform .system ()
149
+ if platform_name not in package ["os" ]:
150
+ handle_error (f"'{ package_name } ' is not available for your platform ({ platform_name } )." )
151
+ url = package ["url" ][platform_name ]
152
+ sha256 = package ["sha256" ][platform_name ]
153
+ if not skip_confirmation :
154
+ confirmation = input (f"Are you sure you want to install '{ package_name } '? (Y/N): " ).strip ().lower ()
155
+ if confirmation != 'y' :
156
+ print (Fore .WHITE + f"Installation of '{ package_name } ' cancelled." + Style .RESET_ALL )
157
+ return
158
+ print (Fore .WHITE + f"Installing { package_name } (v{ package ['version' ]} ) for { platform_name } ..." + Style .RESET_ALL )
143
159
install_path = get_installation_path (package_name )
144
160
install_path .mkdir (parents = True , exist_ok = True )
145
161
download_path = install_path / f"{ package_name } .{ url .split ('.' )[- 1 ]} "
146
162
with tqdm (total = 100 , desc = "Downloading" , unit = '%' , bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt}' ) as pbar :
147
163
urllib .request .urlretrieve (url , download_path , reporthook = lambda count , block_size , total_size : pbar .update (block_size / total_size * 100 ))
148
- print (f"Downloaded { package_name } to { download_path } " )
164
+ print (Fore . WHITE + f"Downloaded { package_name } to { download_path } " + Style . RESET_ALL )
149
165
if not validate_checksum (download_path , sha256 ):
150
166
handle_error (f"Checksum mismatch for { package_name } . Installation aborted." )
151
- if sys .platform == "darwin" and download_path .suffix == '.zip' :
152
- print ("Extracting the package..." )
153
- with zipfile .ZipFile (download_path , 'r' ) as zip_ref :
154
- zip_ref .extractall (install_path )
155
- print (f"Extracted { package_name } to { install_path } " )
156
- download_path .unlink ()
157
- print (f"Removed the ZIP file { download_path } " )
158
167
if install_path .exists ():
159
- print (f"{ package_name } installed successfully!" )
160
-
168
+ print (Fore .GREEN + f"{ package_name } installed successfully!" + Style .RESET_ALL )
161
169
if package ['shortcut' ]:
162
170
target = next (install_path .glob ('*' ), None )
163
171
if target :
164
172
create_shortcut (str (target ), package_name )
165
- print (f"A shortcut for { package_name } has been created on your desktop." )
166
- else :
167
- print (f"Warning: Couldn't find the main executable for { package_name } to create a shortcut." )
168
173
record = read_record ()
169
174
record [package_name ] = {
170
175
"version" : package ["version" ],
171
176
"installed_on" : datetime .now ().isoformat ()
172
177
}
173
178
write_record (record )
174
- else :
175
- handle_error (f"Installation path does not exist after extraction. Installation failed." )
176
179
except FileNotFoundError :
177
180
handle_error ("Package list not found. Try updating the package list using 'update'." )
178
181
except Exception as e :
179
182
handle_error (f"An error occurred during installation: { str (e )} ." )
180
183
181
- def display_help ():
182
- print ("Toolbox Package Manager" )
183
- print ("Version: 2.1.0" )
184
- print ("Usage: toolbox.py <command> <package>" )
185
- print ("Possible commands:" )
186
- print (" list - List available packages" )
187
- print (" install - Install a specified package" )
188
- print (" uninstall - Uninstall a specified package" )
189
- print (" update - Update the package list from the server" )
190
- print (" json - Print the path to the packages.json file" )
191
- print ("License: https://ravendevteam.org/files/BSD-3-Clause.txt" )
192
-
193
- def print_json_path ():
194
- print (f"{ get_package_file_path ()} " )
195
-
196
184
def main ():
197
- package_file_path = get_package_file_path ()
198
- if not package_file_path .exists ():
199
- print ("Package list not found." )
200
- print ("It looks like you need to update the package list." )
201
- print ("Attempting to download the latest packages.json..." )
202
- update_packages ()
203
- if not package_file_path .exists ():
204
- handle_error ("Failed to download the package list." )
205
- if len (sys .argv ) < 2 :
206
- display_help ()
207
- sys .exit (0 )
208
- command = sys .argv [1 ].lower ()
209
- if command == "list" :
185
+ parser = argparse .ArgumentParser (description = "Toolbox Package Manager" )
186
+ parser .add_argument ("command" , choices = ["list" , "install" , "uninstall" , "update" , "help" , "json" ], help = "Command to execute" , nargs = "?" )
187
+ parser .add_argument ("package" , nargs = "?" , help = "Package name (required for install and uninstall)" )
188
+ parser .add_argument ("-y" , "--yes" , action = "store_true" , help = "Skip confirmation prompts" )
189
+ args = parser .parse_args ()
190
+ if not args .command :
191
+ parser .print_help ()
192
+ return
193
+ if args .command == "list" :
210
194
list_packages ()
211
- elif command == "install" :
212
- if len ( sys . argv ) < 3 :
195
+ elif args . command == "install" :
196
+ if not args . package :
213
197
handle_error ("You must specify the package name to install." )
214
- install_package (sys . argv [ 2 ] )
215
- elif command == "uninstall" :
216
- if len ( sys . argv ) < 3 :
198
+ install_package (args . package , args . yes )
199
+ elif args . command == "uninstall" :
200
+ if not args . package :
217
201
handle_error ("You must specify the package name to uninstall." )
218
- uninstall_package (sys . argv [ 2 ] )
219
- elif command == "update" :
202
+ uninstall_package (args . package , args . yes )
203
+ elif args . command == "update" :
220
204
update_packages ()
221
- elif command == "help" :
222
- display_help ()
223
- elif command == "json" :
224
- print_json_path ()
225
- else :
226
- handle_error (f"Unknown command: { command } ." )
205
+ elif args .command == "help" :
206
+ parser .print_help ()
207
+ elif args .command == "json" :
208
+ print (get_package_file_path ())
209
+
227
210
228
211
if __name__ == "__main__" :
229
- main ()
212
+ main ()
0 commit comments