1313from configparser import ConfigParser ,NoOptionError ,NoSectionError
1414from filecmp import cmp
1515from re import sub
16- from shutil import copy
16+ from shutil import copy , rmtree
1717from tempfile import TemporaryDirectory
1818from urllib .error import URLError ,HTTPError
1919from urllib .request import urlopen
@@ -285,6 +285,7 @@ def clone_under(snap, branch):
285285# Delete tree or branch
286286def delete_node (snaps ,quiet ,nuke ):
287287for snap in snaps :
288+ run = False
288289if snap == 0 :
289290print ("F: Changing base snapshot is not allowed." )
290291if not nuke :
@@ -296,7 +297,9 @@ def delete_node(snaps, quiet, nuke):
296297print ("F: Cannot delete deployed snapshot." )
297298elif not quiet and not yes_no (f"Are you sure you want to delete snapshot{ snap } ?" ):
298299sys .exit ("Aborted" )
299- else :
300+ else :
301+ run = True
302+ if nuke or run :
300303children = return_children (fstree ,snap )
301304write_desc ("" ,snap )# Clear description # Why have this? REVIEW 2023
302305os .system (f"btrfs sub del /.snapshots/boot/boot-{ snap } { DEBUG } " )
@@ -354,7 +357,8 @@ def deploy(snap, secondary=False):
354357###2023 os.system(f"btrfs sub create /.snapshots/var/var-{tmp} >/dev/null 2>&1") # REVIEW pretty sure not needed
355358os .system (f"mkdir -p /.snapshots/rootfs/snapshot-{ tmp } /boot{ DEBUG } " )
356359os .system (f"mkdir -p /.snapshots/rootfs/snapshot-{ tmp } /etc{ DEBUG } " )
357- os .system (f"rm -rf /.snapshots/rootfs/snapshot-{ tmp } /var{ DEBUG } " )
360+ ###TODEL os.system(f"rm -rf /.snapshots/rootfs/snapshot-{tmp}/var{DEBUG}")
361+ rmrf (f"/.snapshots/rootfs/snapshot-{ tmp } /var" )
358362os .system (f"cp -r --reflink=auto /.snapshots/boot/boot-{ snap } /. /.snapshots/rootfs/snapshot-{ tmp } /boot{ DEBUG } " )
359363os .system (f"cp -r --reflink=auto /.snapshots/etc/etc-{ snap } /. /.snapshots/rootfs/snapshot-{ tmp } /etc{ DEBUG } " )
360364# os.system(f"cp --reflink=auto -r /.snapshots/var/var-{etc}/* /.snapshots/var/var-{tmp} >/dev/null 2>&1") # REVIEW 2023 pretty sure not needed
@@ -708,9 +712,9 @@ def post_transactions(snap):
708712# Some operations were moved below to fix hollow functionality ### IMPORTANT REVIEW 2023
709713# File operations in snapshot-chr
710714# os.system(f"btrfs sub del /.snapshots/rootfs/snapshot-{snap}{DEBUG}") ### REVIEW # Moved to a few lines below ### GOOD_TO_DELETE_2023
711- os . system (f"rm -rf /.snapshots/boot/boot-chr{ snap } /* { DEBUG } " )
715+ rmrf (f"/.snapshots/boot/boot-chr{ snap } " , "/* " )
712716os .system (f"cp -r --reflink=auto /.snapshots/rootfs/snapshot-chr{ snap } /boot/. /.snapshots/boot/boot-chr{ snap } { DEBUG } " )
713- os . system (f"rm -rf /.snapshots/etc/etc-chr{ snap } /* { DEBUG } " )
717+ rmrf (f"/.snapshots/etc/etc-chr{ snap } " , "/* " )
714718os .system (f"cp -r --reflink=auto /.snapshots/rootfs/snapshot-chr{ snap } /etc/. /.snapshots/etc/etc-chr{ snap } { DEBUG } " )
715719# Keep package manager's cache after installing packages. This prevents unnecessary downloads for each snapshot when upgrading multiple snapshots
716720cache_copy (snap ,"post_transactions" )
@@ -894,6 +898,20 @@ def return_children(tree, id):
894898children .remove (id )
895899return children
896900
901+ # Pythonic rm -rf (for both deleting just contents and everything)
902+ def rmrf (a_path ,contents = "" ):
903+ if contents == "/*" :# just delete a_path/*
904+ for root ,dirs ,files in os .walk (a_path ):
905+ for f in files :
906+ os .unlink (os .path .join (root ,f ))
907+ for d in dirs :
908+ rmtree (os .path .join (root ,d ))
909+ else :# delete everything
910+ if os .path .isfile (a_path ):
911+ os .unlink (a_path )
912+ elif os .path .isdir (a_path ):
913+ rmtree (a_path )
914+
897915# Rollback last booted deployment
898916def rollback ():
899917tmp = get_tmp ()
@@ -1158,18 +1176,18 @@ def tree_sync(tree, tname, force_offline, live):
11581176# Sync tree helper function ### REVIEW might need to put it in distribution-specific ashpk.py
11591177def tree_sync_helper (CHR ,s_f ,s_t ):
11601178os .system ("mkdir -p /.snapshots/tmp-db/local/" )### REVIEW Still resembling Arch pacman folder structure!
1161- os . system ( "rm -rf /.snapshots/tmp-db/local/*" )### REVIEW
1179+ rmrf ( " /.snapshots/tmp-db/local" , " /*" )### REVIEW
11621180pkg_list_from = pkg_list (s_f ,"" )
11631181pkg_list_to = pkg_list (s_t ,CHR )
11641182# Get packages to be inherited
11651183pkg_list_from = [j for j in pkg_list_from if j not in pkg_list_to ]
11661184os .system (f"cp -r /.snapshots/rootfs/snapshot-{ CHR } { s_t } /usr/share/ash/db/local/. /.snapshots/tmp-db/local/" )### REVIEW
11671185os .system (f"cp -n -r --reflink=auto /.snapshots/rootfs/snapshot-{ s_f } /. /.snapshots/rootfs/snapshot-{ CHR } { s_t } /{ DEBUG } " )
1168- os . system (f"rm -rf /.snapshots/rootfs/snapshot-{ CHR } { s_t } /usr/share/ash/db/local/*" )### REVIEW
1186+ rmrf (f"/.snapshots/rootfs/snapshot-{ CHR } { s_t } /usr/share/ash/db/local" , " /*" )### REVIEW
11691187os .system (f"cp -r /.snapshots/tmp-db/local/. /.snapshots/rootfs/snapshot-{ CHR } { s_t } /usr/share/ash/db/local/" )### REVIEW
11701188for entry in pkg_list_from :
11711189os .system (f"bash -c 'cp -r /.snapshots/rootfs/snapshot-{ s_f } /usr/share/ash/db/local/{ entry } -[0-9]* /.snapshots/rootfs/snapshot-{ CHR } { s_t } /usr/share/ash/db/local/'" )### REVIEW
1172- os . system ( "rm -rf /.snapshots/tmp-db/local/*" )
1190+ rmrf ( " /.snapshots/tmp-db/local" , " /*" )
11731191
11741192# Recursively run an update in tree
11751193def tree_upgrade (tree ,tname ):
@@ -1317,7 +1335,7 @@ def main():
13171335elif sys .platform .startswith ("cosmo" )and os .getuid ()!= 0 :### TODO use geteuid when fixed
13181336exit ("F: Run ash with super user privileges!" )
13191337elif sys .platform .startswith ("windows" ):
1320- from ctypes import windll # type: ignore
1338+ from ctypes import windll # type: ignore ### REVIEW 2023
13211339if windll .shell32 .IsUserAnAdmin ()!= 0 :
13221340exit ("F: Run ash with admin privileges!" )
13231341elif sys .platform .startswith ("darwin" ):
@@ -1333,7 +1351,9 @@ def main():
13331351# print("Please don't use ash inside a chroot!") ### TODO
13341352# Recognize argument and call appropriate function
13351353parser = ArgumentParser (prog = 'ash' ,description = 'Any Snapshot Hierarchical OS' )
1336- subparsers = parser .add_subparsers (dest = 'command' ,required = True ,help = 'Different commands for ash' )
1354+ #subparsers = parser.add_subparsers(dest='command', required=True, help='Different commands for ash')
1355+ subparsers = parser .add_subparsers (dest = 'command' ,help = 'Different commands for ash' )
1356+ subparsers .required = True # backward compatibility with python <= 3.6
13371357# Ash update
13381358upme_par = subparsers .add_parser ("upme" ,aliases = ['ash-update' ],allow_abbrev = True ,help = "Update ash itself" )
13391359upme_par .add_argument ('--dbg' ,'--debug' ,'--test' ,'-d' ,'-t' ,action = 'store_true' ,required = False ,help = 'Enable live install for snapshot' )
@@ -1523,11 +1543,11 @@ def main():
15231543whichtmp_par = subparsers .add_parser ("whichtmp" ,aliases = ["whichdep" ,"which" ],allow_abbrev = True ,help = "Show which deployment snapshot is in use" )
15241544whichtmp_par .set_defaults (func = lambda :get_tmp (console = True ))# print to console REVIEW 2023 NEWER: test print(get_tmp)
15251545# Which snapshot(s) contain a package
1526- which_snap_par = subparsers .add_parser ("whichsnap" ,aliases = ["ws" ],allow_abbrev = True ,help = 'Get list of installed packages in a snapshot' )
1527- which_snap_par .add_argument ('pkg' ,nargs = '+' ,help = 'a package' )
1546+ which_snap_par = subparsers .add_parser ("whichsnap" ,aliases = ["ws" ],allow_abbrev = True ,help = 'Which snapshot has a package installed' )
1547+ which_snap_par .add_argument ('--pkg' ,'--package' ,'-p' ,nargs = '+' ,required = True ,help = 'a package' )
1548+ #which_snap_par.add_argument('pkg', nargs='+', help='a package')
15281549which_snap_par .set_defaults (func = which_snapshot_has )
15291550# Call relevant functions
1530- #args_1 = parser.parse_args()
15311551args_1 = parser .parse_args (args = None if sys .argv [1 :]else ['--help' ])# Show help if no command used
15321552args_2 = vars (args_1 ).copy ()
15331553args_2 .pop ('command' ,None )