@@ -29,11 +29,19 @@ from shlex import quote
2929import xml .etree .ElementTree as ET
3030
3131# Constants
32- ZYPPERONI_VERSION = "1.0.1 "
32+ ZYPPERONI_VERSION = "1.1.0 "
3333ZYPPER_PID_FILE = "/run/zypp.pid"
34+ ZYPPER_ENV = "ZYPP_CURL2=1 ZYPP_PCK_PRELOAD=1 ZYPP_SINGLE_RPMTRANS=1"
3435
3536################################
3637
38+ # Function to get zypper version
39+ def zypper_version ():
40+ out , ret = shell_exec ("zypper --version" )
41+ version = out .split ()[1 ].split ("." )
42+ version = [int (x ) for x in version ] # MAJOR.MINOR.PATCH
43+ return version
44+
3745# Function to get ANSI colored text
3846def color (text_type , text ):
3947 # rgb color codes
@@ -47,10 +55,12 @@ def color(text_type, text):
4755 }
4856 color = colors .get (text_type )
4957 # color only if running in terminal and color output is not disabled
50- if color and sys .stdout .isatty () and args and not args .no_color :
51- return f"\033 [38;2;{ color [0 ]} ;{ color [1 ]} ;{ color [2 ]} m{ text } \033 [38;2;255;255;255m"
52- else :
53- return text
58+ try :
59+ if color and sys .stdout .isatty () and args and not args .no_color :
60+ return f"\033 [38;2;{ color [0 ]} ;{ color [1 ]} ;{ color [2 ]} m{ text } \033 [38;2;255;255;255m"
61+ except NameError :
62+ pass
63+ return text
5464
5565# Function to query user for yes or no
5666def query_yes_no (question , default = None ):
@@ -89,6 +99,10 @@ def release_zypp_lock():
8999
90100# Function to unmount temp dirs provided list of UUIDs
91101def unmount (UUID ):
102+ try :
103+ ZYPPERONI_TMP_DIR
104+ except NameError :
105+ return False
92106 umount_counter = 0
93107 while umount_counter < len (UUID ):
94108 umount_counter = 0
@@ -110,7 +124,14 @@ def unmount(UUID):
110124# Function to recursively delete temp files
111125def recursive_delete (path ):
112126 # perform some sanity checks
113- if not path .startswith (ZYPPERONI_TMP_DIR ):
127+ try :
128+ ZYPPERONI_TMP_DIR
129+ except NameError :
130+ return False
131+ if not path .startswith ("/tmp" ) or not path .startswith (ZYPPERONI_TMP_DIR ):
132+ return False
133+ out , ret = shell_exec ("mount -l | grep -c '/tmp/zypperoni_'" )
134+ if int (out ) > 0 :
114135 return False
115136 command = f"rm -r { quote (path )} > /dev/null 2>&1"
116137 os .system (command )
@@ -119,7 +140,10 @@ def recursive_delete(path):
119140# Function to cleanup on zypperoni exit
120141def zypperoni_cleanup ():
121142 release_zypp_lock ()
122- recursive_delete (ZYPPERONI_TMP_DIR )
143+ try :
144+ recursive_delete (ZYPPERONI_TMP_DIR )
145+ except NameError :
146+ pass
123147
124148# Function to get output and exit code of shell command
125149def shell_exec (command ):
@@ -128,50 +152,32 @@ def shell_exec(command):
128152 return output .strip (), res .returncode
129153
130154# Async function to perform zypper shell commands
131- async def zypper_task (lock , UUID , task_type , task_item , total_items , item_counter ):
155+ async def zypper_task (lock , UUID , args , task_item , total_items , item_counter ):
132156 try :
133157 async with lock :
134158 uuid = UUID .pop ()
135159 log_messages = {}
136160 commands = ""
137161 temp_dir = f"{ ZYPPERONI_TMP_DIR } /{ uuid } /rootfs"
138- if task_type == "ref" :
139- log_messages .update ({"start" : f"Refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
140- log_messages .update ({"success" : f"Successfully refreshed repo { task_item !r} " })
141- log_messages .update ({"error" : f"Error refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
142- log_messages .update ({"exception" : f"Received SIGINT while refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
143- if not os .path .isdir (temp_dir ):
144- commands = refresh_mount_commands + refresh_shell_commands
145- commands = commands .format (
146- uuid = uuid ,
147- refresh_type = "refresh" ,
148- repo_alias = task_item ,
149- )
150- else :
151- commands = refresh_shell_commands .format (
152- uuid = uuid ,
153- refresh_type = "refresh" ,
154- repo_alias = task_item ,
155- )
156- elif task_type == "force-ref" :
157- log_messages .update ({"start" : f"Force refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
158- log_messages .update ({"success" : f"Successfully force refreshed repo { task_item !r} " })
159- log_messages .update ({"error" : f"Error force refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
160- log_messages .update ({"exception" : f"Received SIGINT while force refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
162+ if args .command_name in ["refresh" , "ref" ]:
163+ log_messages .update ({"start" : f"{ 'Force ' if args .force else '' } Refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
164+ log_messages .update ({"success" : f"Successfully { 'force ' if args .force else '' } refreshed repo { task_item !r} " })
165+ log_messages .update ({"error" : f"Error { 'force ' if args .force else '' } refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
166+ log_messages .update ({"exception" : f"Received SIGINT while { 'force ' if args .force else '' } refreshing repo [{ item_counter } /{ total_items } ] { task_item !r} " })
161167 if not os .path .isdir (temp_dir ):
162168 commands = refresh_mount_commands + refresh_shell_commands
163169 commands = commands .format (
164170 uuid = uuid ,
165- refresh_type = "refresh --force" ,
171+ refresh_type = "refresh --force" if args . force else "refresh" ,
166172 repo_alias = task_item ,
167173 )
168174 else :
169175 commands = refresh_shell_commands .format (
170176 uuid = uuid ,
171- refresh_type = "refresh --force" ,
177+ refresh_type = "refresh --force" if args . force else "refresh" ,
172178 repo_alias = task_item ,
173179 )
174- elif task_type in ["dup " , "dup-download " , "in " , "in-download " , "inr " , "inr-download " ]:
180+ elif args . command_name in ["dist-upgrade " , "dup" , "install " , "in" , "install-new-recommends " , "inr" ]:
175181 log_messages .update ({"start" : f"Downloading package [{ item_counter } /{ total_items } ] { task_item !r} " })
176182 log_messages .update ({"success" : f"Successfully downloaded package { task_item !r} " })
177183 log_messages .update ({"error" : f"Error downloading package [{ item_counter } /{ total_items } ] { task_item !r} " })
@@ -208,14 +214,14 @@ async def zypper_task(lock, UUID, task_type, task_item, total_items, item_counte
208214 logging .debug (log_messages .get ("exception" ))
209215
210216# Async function to perform multiple tasks concurrently
211- async def main_task (num_jobs , task_type , task_items , no_confirm = None ):
217+ async def main_task (task_items , args ):
212218 EXCEPTION_OCCUR = False
213219 # init array of temp dir UUIDs corresponding to max num of jobs
214- UUID = [f"{ uuid4 ()!s} " for _ in range (num_jobs )]
220+ UUID = [f"{ uuid4 ()!s} " for _ in range (args . jobs )]
215221 UUID_UNCHANGED = UUID .copy ()
216222 total_items = len (task_items )
217223 item_counter = 0
218- if task_type == " in" :
224+ if args . command_name in [ "install" , " in"] :
219225 install_pkgs = task_items .copy ()
220226 try :
221227 # start processing tasks
@@ -227,13 +233,11 @@ async def main_task(num_jobs, task_type, task_items, no_confirm=None):
227233 log_messages = {}
228234 task_item = task_items .pop (0 )
229235 item_counter += 1
230- if task_type == "ref" :
231- log_messages .update ({"exception" : "Received SIGINT while processing tasks to refresh repo" })
232- elif task_type == "force-ref" :
233- log_messages .update ({"exception" : "Received SIGINT while processing tasks to force refresh repo" })
234- elif task_type in ["dup" , "dup-download" , "in" , "in-download" , "inr" , "inr-download" ]:
236+ if args .command_name in ["refresh" , "ref" ]:
237+ log_messages .update ({"exception" : f"Received SIGINT while processing tasks to { 'force ' if args .force else '' } refresh repo" })
238+ elif args .command_name in ["dist-upgrade" , "dup" , "install" , "in" , "install-new-recommends" , "inr" ]:
235239 log_messages .update ({"exception" : "Received SIGINT while processing tasks to download packages" })
236- asyncio .create_task (zypper_task (lock , UUID , task_type , task_item , total_items , item_counter ))
240+ asyncio .create_task (zypper_task (lock , UUID , args , task_item , total_items , item_counter ))
237241 await asyncio .sleep (0.1 )
238242 # finished processing all tasks
239243 tasks = asyncio .all_tasks ()
@@ -258,19 +262,19 @@ async def main_task(num_jobs, task_type, task_items, no_confirm=None):
258262 # release zypper exclusive lock
259263 release_zypp_lock ()
260264 # perform additional zypper commands (if any) on no exception
261- if not EXCEPTION_OCCUR :
265+ if not EXCEPTION_OCCUR and \
266+ args .command_name in ["dist-upgrade" , "dup" , "install" , "in" , "install-new-recommends" , "inr" ] and \
267+ not args .download_only :
262268 msg = "Zypperoni has finished its tasks. Handing you over to zypper..."
263- if task_type == "dup" :
264- logging . info ( color ( "info " , msg ))
265- command = f"env ZYPP_SINGLE_RPMTRANS=1 zypper { '--non-interactive' if no_confirm else '' } --no-cd dist-upgrade"
269+ logging . info ( color ( "info" , msg ))
270+ if args . command_name in [ "dist-upgrade " , "dup" ]:
271+ command = f"env { ZYPPER_ENV } zypper { '--non-interactive' if args . no_confirm else '' } --no-cd dist-upgrade"
266272 os .system (command )
267- elif task_type == "in" :
268- logging .info (color ("info" , msg ))
269- command = f"env ZYPP_SINGLE_RPMTRANS=1 zypper { '--non-interactive' if no_confirm else '' } --no-cd install { ' ' .join (install_pkgs )} "
273+ elif args .command_name in ["install" , "in" ]:
274+ command = f"env { ZYPPER_ENV } zypper { '--non-interactive' if args .no_confirm else '' } --no-cd install { ' ' .join (install_pkgs )} "
270275 os .system (command )
271- elif task_type == "inr" :
272- logging .info (color ("info" , msg ))
273- command = f"env ZYPP_SINGLE_RPMTRANS=1 zypper { '--non-interactive' if no_confirm else '' } --no-cd install-new-recommends"
276+ elif args .command_name in ["install-new-recommends" , "inr" ]:
277+ command = f"env { ZYPPER_ENV } zypper { '--non-interactive' if args .no_confirm else '' } --no-cd install-new-recommends"
274278 os .system (command )
275279
276280################################
@@ -396,7 +400,7 @@ mount -o bind,ro /var/lib/ca-certificates {ZYPPERONI_TMP_DIR}/{{uuid}}/rootfs/va
396400
397401# Shell commands to perform zypper refresh / force-refresh
398402refresh_shell_commands = f"""
399- chroot { ZYPPERONI_TMP_DIR } /{{uuid}}/rootfs env -i zypper --non-interactive {{refresh_type}} {{repo_alias}};
403+ chroot { ZYPPERONI_TMP_DIR } /{{uuid}}/rootfs env -i { ZYPPER_ENV } zypper --non-interactive {{refresh_type}} {{repo_alias}};
400404"""
401405
402406# Shell commands to prepare temp mounts for zypper download
@@ -420,7 +424,7 @@ mount -o bind,ro /var/lib/ca-certificates {ZYPPERONI_TMP_DIR}/{{uuid}}/rootfs/va
420424
421425# Shell commands to perform zypper download
422426download_shell_commands = f"""
423- chroot { ZYPPERONI_TMP_DIR } /{{uuid}}/rootfs env -i zypper --non-interactive download {{pkg_name}};
427+ chroot { ZYPPERONI_TMP_DIR } /{{uuid}}/rootfs env -i { ZYPPER_ENV } zypper --non-interactive download {{pkg_name}};
424428"""
425429
426430# Dirs to unmount (one per line)
@@ -439,7 +443,7 @@ if args.command_name in ["refresh", "ref"]:
439443 # get all enabled repos
440444 logging .info (color ("info" , "Getting all enabled repos" ))
441445 REPO_ALIAS = []
442- xml_output , ret = shell_exec ("env -i zypper --non-interactive --no-cd --xmlout repos" )
446+ xml_output , ret = shell_exec (f "env -i { ZYPPER_ENV } zypper --non-interactive --no-cd --xmlout repos" )
443447 logging .debug (xml_output )
444448 get_zypp_lock ()
445449 docroot = ET .fromstring (xml_output )
@@ -452,7 +456,7 @@ if args.command_name in ["refresh", "ref"]:
452456 zypperoni_cleanup ()
453457 sys .exit ()
454458 try :
455- asyncio .run (main_task (args . jobs , "force-ref" if args . force else "ref" , REPO_ALIAS ))
459+ asyncio .run (main_task (REPO_ALIAS , args ))
456460 except asyncio .exceptions .CancelledError :
457461 logging .debug ("Received SIGINT for asyncio runner" )
458462 except :
@@ -462,7 +466,7 @@ if args.command_name in ["refresh", "ref"]:
462466elif args .command_name in ["dist-upgrade" , "dup" ]:
463467 # get info about dup packages
464468 logging .info (color ("info" , "Getting all packages to be downloaded for distribution upgrade" ))
465- xml_output , ret = shell_exec ("env -i zypper --non-interactive --no-cd --xmlout dist-upgrade --dry-run" )
469+ xml_output , ret = shell_exec (f "env -i { ZYPPER_ENV } zypper --non-interactive --no-cd --xmlout dist-upgrade --dry-run" )
466470 logging .debug (xml_output )
467471 if ret == 0 and xml_output .find ("Nothing to do" ) != - 1 :
468472 logging .info (color ("info" , "Nothing to do. Exiting..." ))
@@ -498,7 +502,7 @@ elif args.command_name in ["dist-upgrade", "dup"]:
498502 logging .warning (color ("warning" , msg ))
499503 # get info about dup packages from 'zypper lu'
500504 logging .info (color ("info" , "Getting all packages to be upgraded" ))
501- xml_output , ret = shell_exec ("env -i zypper --non-interactive --no-cd --xmlout list-updates --type package --all" )
505+ xml_output , ret = shell_exec (f "env -i { ZYPPER_ENV } zypper --non-interactive --no-cd --xmlout list-updates --type package --all" )
502506 logging .debug (xml_output )
503507 docroot = ET .fromstring (xml_output )
504508 # parse all packages from xml output
@@ -520,15 +524,15 @@ elif args.command_name in ["dist-upgrade", "dup"]:
520524 if not args .download_only and download_size_bytes == 0 :
521525 zypperoni_cleanup ()
522526 logging .info (color ("info" , "Zypperoni has finished its tasks. Handing you over to zypper..." ))
523- command = f"env ZYPP_SINGLE_RPMTRANS=1 zypper { '--non-interactive' if args .no_confirm else '' } --no-cd dist-upgrade"
527+ command = f"env { ZYPPER_ENV } zypper { '--non-interactive' if args .no_confirm else '' } --no-cd dist-upgrade"
524528 os .system (command )
525529 sys .exit ()
526530 logging .info (color ("info" , f"Packages to download: { ' ' .join (DUP_PKG )} " ))
527531 if not args .no_confirm and not query_yes_no ("Would you like to continue?" , default = "yes" ):
528532 zypperoni_cleanup ()
529533 sys .exit ()
530534 try :
531- asyncio .run (main_task (args . jobs , "dup-download" if args . download_only else "dup" , DUP_PKG , args . no_confirm ))
535+ asyncio .run (main_task (DUP_PKG , args ))
532536 except asyncio .exceptions .CancelledError :
533537 logging .debug ("Received SIGINT for asyncio runner" )
534538 except :
@@ -538,7 +542,7 @@ elif args.command_name in ["dist-upgrade", "dup"]:
538542elif args .command_name in ["install" , "in" ]:
539543 # get info about install packages
540544 logging .info (color ("info" , "Getting packages and their dependencies to be downloaded for installation" ))
541- xml_output , ret = shell_exec (f"env -i zypper --non-interactive --no-cd --xmlout install --dry-run { ' ' .join (args .package )} " )
545+ xml_output , ret = shell_exec (f"env -i { ZYPPER_ENV } zypper --non-interactive --no-cd --xmlout install --dry-run { ' ' .join (args .package )} " )
542546 logging .debug (xml_output )
543547 if ret == 0 and xml_output .find ("Nothing to do" ) != - 1 :
544548 logging .info (color ("info" , "Nothing to do. Exiting..." ))
@@ -596,15 +600,15 @@ elif args.command_name in ["install", "in"]:
596600 if not args .download_only and download_size_bytes == 0 :
597601 zypperoni_cleanup ()
598602 logging .info (color ("info" , "Zypperoni has finished its tasks. Handing you over to zypper..." ))
599- command = f"env ZYPP_SINGLE_RPMTRANS=1 zypper { '--non-interactive' if args .no_confirm else '' } --no-cd install { ' ' .join (args .package )} "
603+ command = f"env { ZYPPER_ENV } zypper { '--non-interactive' if args .no_confirm else '' } --no-cd install { ' ' .join (args .package )} "
600604 os .system (command )
601605 sys .exit ()
602606 logging .info (color ("info" , f"Packages to download: { ' ' .join (IN_PKG )} " ))
603607 if not args .no_confirm and not query_yes_no ("Would you like to continue?" , default = "yes" ):
604608 zypperoni_cleanup ()
605609 sys .exit ()
606610 try :
607- asyncio .run (main_task (args . jobs , "in-download" if args . download_only else "in" , IN_PKG , args . no_confirm ))
611+ asyncio .run (main_task (IN_PKG , args ))
608612 except asyncio .exceptions .CancelledError :
609613 logging .debug ("Received SIGINT for asyncio runner" )
610614 except :
@@ -614,7 +618,7 @@ elif args.command_name in ["install", "in"]:
614618elif args .command_name in ["install-new-recommends" , "inr" ]:
615619 # get info about recommended install packages
616620 logging .info (color ("info" , "Getting new packages and their dependencies to be downloaded for recommended installation" ))
617- xml_output , ret = shell_exec (f"env -i zypper --non-interactive --no-cd --xmlout install-new-recommends --dry-run" )
621+ xml_output , ret = shell_exec (f"env -i { ZYPPER_ENV } zypper --non-interactive --no-cd --xmlout install-new-recommends --dry-run" )
618622 logging .debug (xml_output )
619623 if ret == 0 and xml_output .find ("Nothing to do" ) != - 1 :
620624 logging .info (color ("info" , "Nothing to do. Exiting..." ))
@@ -663,15 +667,15 @@ elif args.command_name in ["install-new-recommends", "inr"]:
663667 if not args .download_only and download_size_bytes == 0 :
664668 zypperoni_cleanup ()
665669 logging .info (color ("info" , "Zypperoni has finished its tasks. Handing you over to zypper..." ))
666- command = f"env ZYPP_SINGLE_RPMTRANS=1 zypper { '--non-interactive' if args .no_confirm else '' } --no-cd install-new-recommends"
670+ command = f"env { ZYPPER_ENV } zypper { '--non-interactive' if args .no_confirm else '' } --no-cd install-new-recommends"
667671 os .system (command )
668672 sys .exit ()
669673 logging .info (color ("info" , f"Packages to download: { ' ' .join (INR_PKG )} " ))
670674 if not args .no_confirm and not query_yes_no ("Would you like to continue?" , default = "yes" ):
671675 zypperoni_cleanup ()
672676 sys .exit ()
673677 try :
674- asyncio .run (main_task (args . jobs , "inr-download" if args . download_only else "inr" , INR_PKG , args . no_confirm ))
678+ asyncio .run (main_task (INR_PKG , args ))
675679 except asyncio .exceptions .CancelledError :
676680 logging .debug ("Received SIGINT for asyncio runner" )
677681 except :
0 commit comments