diff options
author | V3n3RiX <venerix@koprulu.sector> | 2024-02-26 13:02:07 +0000 |
---|---|---|
committer | V3n3RiX <venerix@koprulu.sector> | 2024-02-26 13:02:07 +0000 |
commit | 94c0bd2dfeb801087c7a03787a964b15b2a37bd8 (patch) | |
tree | 63a7276179e074609173b7c3a2540e2e290d0f26 | |
parent | 7d32051b5114160c66c354972f8a64a00d331c6c (diff) |
* extend https://bugs.redcorelinux.org/show_bug.cgi?id=143v6.2402.1
* improve the UX at package removal stage and orphan removal stage
* require double confirmation for a forced, unsafe removal of a package
-rw-r--r-- | src/backend/autormpkgsrc.py | 74 | ||||
-rw-r--r-- | src/backend/rmpkgsrc.py | 161 |
2 files changed, 197 insertions, 38 deletions
diff --git a/src/backend/autormpkgsrc.py b/src/backend/autormpkgsrc.py index 3f0c852..d696757 100644 --- a/src/backend/autormpkgsrc.py +++ b/src/backend/autormpkgsrc.py @@ -1,8 +1,11 @@ #!/usr/bin/python3 import atexit +import fcntl import io +import os import signal +import selectors import subprocess import sys import sisyphus.checkenv @@ -11,6 +14,26 @@ import sisyphus.killemerge import sisyphus.syncdb +def set_nonblocking(fd): + flags = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + +def spinner_animation(): + spinner = ['-', '\\', '|', '/'] + sel = selectors.DefaultSelector() + sel.register(sys.stdin, selectors.EVENT_READ) + + for _ in range(10): + for char in spinner: + sys.stdout.write('\b' + char) + sys.stdout.flush() + events = sel.select(timeout=0.1) + if events: + return + sys.stdout.write('\b') + + def sigint_handler(signal, frame): sys.exit(0) @@ -22,17 +45,46 @@ def start(gfx_ui=False): args = ['--quiet', '--depclean'] if sisyphus.checkenv.root() and not gfx_ui: - p_exe = subprocess.Popen(['emerge'] + args + ['--ask']) - try: - p_exe.wait() - sisyphus.syncdb.lcl_tbl() - except KeyboardInterrupt: - p_exe.terminate() - try: - p_exe.wait(1) - except subprocess.TimeoutExpired: - p_exe.kill() - sys.exit() + print("\n" + sisyphus.getclr.bright_white + "Orphaned, no longer needed packages are slated for" + sisyphus.getclr.reset + " " + + sisyphus.getclr.green + "'safe'" + sisyphus.getclr.reset + " " + sisyphus.getclr.bright_white + "removal." + sisyphus.getclr.reset + "\n") + while True: + user_input = input(sisyphus.getclr.bright_white + "Would you like to proceed?" + sisyphus.getclr.reset + " " + + "[" + sisyphus.getclr.bright_green + "Yes" + sisyphus.getclr.reset + "/" + sisyphus.getclr.bright_red + "No" + sisyphus.getclr.reset + "]" + " ") + if user_input.lower() in ['yes', 'y', '']: + p_exe = subprocess.Popen(['emerge'] + args) + try: + set_nonblocking(sys.stdout.fileno()) + spinner_animation() + + sel = selectors.DefaultSelector() + sel.register(sys.stdin, selectors.EVENT_READ) + + while True: + events = sel.select(timeout=0.1) + for key, mask in events: + if key.fileobj == sys.stdin: + line = sys.stdin.readline().strip() + if line.lower() == 'q': + sys.exit() + if p_exe.poll() is not None: + break + except KeyboardInterrupt: + p_exe.terminate() + try: + p_exe.wait(1) + except subprocess.TimeoutExpired: + p_exe.kill() + sys.exit() + finally: + p_exe.wait() + sisyphus.syncdb.lcl_tbl() + break + elif user_input.lower() in ['no', 'n']: + break + else: + print("\nSorry, response" + " " + "'" + + user_input + "'" + " " + "not understood.\n") + continue elif gfx_ui: p_exe = subprocess.Popen( ['emerge'] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/src/backend/rmpkgsrc.py b/src/backend/rmpkgsrc.py index 17d5a12..1a7ecd6 100644 --- a/src/backend/rmpkgsrc.py +++ b/src/backend/rmpkgsrc.py @@ -1,10 +1,12 @@ #!/usr/bin/python3 import atexit +import fcntl import io import os import pickle import signal +import selectors import subprocess import sys import sisyphus.checkenv @@ -15,6 +17,26 @@ import sisyphus.solverevdeps import sisyphus.syncdb +def set_nonblocking(fd): + flags = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + +def spinner_animation(): + spinner = ['-', '\\', '|', '/'] + sel = selectors.DefaultSelector() + sel.register(sys.stdin, selectors.EVENT_READ) + + for _ in range(10): + for char in spinner: + sys.stdout.write('\b' + char) + sys.stdout.flush() + events = sel.select(timeout=0.1) + if events: + return + sys.stdout.write('\b') + + def sigint_handler(signal, frame): sys.exit(0) @@ -54,7 +76,21 @@ def start(pkgname, depclean=False, gfx_ui=False, unmerge=False): p_exe = subprocess.Popen( ['emerge'] + args + ['--pretend', '--verbose'] + list(pkgname)) try: - p_exe.wait() + set_nonblocking(sys.stdout.fileno()) + spinner_animation() + + sel = selectors.DefaultSelector() + sel.register(sys.stdin, selectors.EVENT_READ) + + while True: + events = sel.select(timeout=0.1) + for key, mask in events: + if key.fileobj == sys.stdin: + line = sys.stdin.readline().strip() + if line.lower() == 'q': + sys.exit() + if p_exe.poll() is not None: + break except KeyboardInterrupt: p_exe.terminate() try: @@ -62,24 +98,66 @@ def start(pkgname, depclean=False, gfx_ui=False, unmerge=False): except subprocess.TimeoutExpired: p_exe.kill() sys.exit() + finally: + p_exe.wait() print(sisyphus.getclr.bright_red + "\nWon't uninstall! Other packages depend on " + sisyphus.getclr.reset + str(pkgname)) - print(sisyphus.getclr.bright_red + "Use the " + sisyphus.getclr.reset + sisyphus.getclr.green + "'--force'" + - sisyphus.getclr.reset + sisyphus.getclr.bright_red + " option to override at your own risk!\n" + sisyphus.getclr.reset) + print(sisyphus.getclr.bright_white + "Use the " + sisyphus.getclr.reset + sisyphus.getclr.green + "'--force'" + + sisyphus.getclr.reset + sisyphus.getclr.bright_white + " option to override at your own risk!\n" + sisyphus.getclr.reset) else: if unmerge: - p_exe = subprocess.Popen( - ['emerge', '--quiet', '--unmerge', '--ask'] + list(pkgname)) - try: - p_exe.wait() - sisyphus.syncdb.lcl_tbl() - except KeyboardInterrupt: - p_exe.terminate() - try: - p_exe.wait(1) - except subprocess.TimeoutExpired: - p_exe.kill() - sys.exit() + print("\n" + sisyphus.getclr.bright_white + "Selected packages are slated for" + sisyphus.getclr.reset + " " + sisyphus.getclr.green + + "'forced'" + sisyphus.getclr.reset + " " + sisyphus.getclr.bright_white + "removal." + sisyphus.getclr.reset + "\n") + while True: + user_input = input(sisyphus.getclr.bright_white + "Would you like to proceed?" + sisyphus.getclr.reset + " " + + "[" + sisyphus.getclr.bright_green + "Yes" + sisyphus.getclr.reset + "/" + sisyphus.getclr.bright_red + "No" + sisyphus.getclr.reset + "]" + " ") + if user_input.lower() in ['yes', 'y', '']: + while True: + confirmation_input = input(sisyphus.getclr.bright_white + "Are you sure you would like to proceed?" + sisyphus.getclr.reset + " " + + "[" + sisyphus.getclr.bright_green + "Yes" + sisyphus.getclr.reset + "/" + sisyphus.getclr.bright_red + "No" + sisyphus.getclr.reset + "]" + " ") + if confirmation_input.lower() in ['yes', 'y', '']: + p_exe = subprocess.Popen( + ['emerge', '--quiet', '--unmerge'] + list(pkgname)) + try: + set_nonblocking(sys.stdout.fileno()) + spinner_animation() + + sel = selectors.DefaultSelector() + sel.register(sys.stdin, selectors.EVENT_READ) + + while True: + events = sel.select(timeout=0.1) + for key, mask in events: + if key.fileobj == sys.stdin: + line = sys.stdin.readline().strip() + if line.lower() == 'q': + sys.exit() + if p_exe.poll() is not None: + break + except KeyboardInterrupt: + p_exe.terminate() + try: + p_exe.wait(1) + except subprocess.TimeoutExpired: + p_exe.kill() + sys.exit() + finally: + p_exe.wait() + sisyphus.syncdb.lcl_tbl() + break + elif confirmation_input.lower() in ['no', 'n']: + break + else: + print("\nSorry, response" + " " + "'" + + confirmation_input + "'" + " " + "not understood.\n") + continue + break + elif user_input.lower() in ['no', 'n']: + break + else: + print("\nSorry, response" + " " + "'" + + user_input + "'" + " " + "not understood.\n") + continue elif depclean: if gfx_ui: p_exe = subprocess.Popen( @@ -93,15 +171,44 @@ def start(pkgname, depclean=False, gfx_ui=False, unmerge=False): p_exe.wait() sisyphus.syncdb.lcl_tbl() else: - p_exe = subprocess.Popen( - ['emerge'] + args + ['--ask'] + list(pkgname)) - try: - p_exe.wait() - sisyphus.syncdb.lcl_tbl() - except KeyboardInterrupt: - p_exe.terminate() - try: - p_exe.wait(1) - except subprocess.TimeoutExpired: - p_exe.kill() - sys.exit() + print("\n" + sisyphus.getclr.bright_white + "Selected packages are slated for" + sisyphus.getclr.reset + " " + sisyphus.getclr.green + + "'safe'" + sisyphus.getclr.reset + " " + sisyphus.getclr.bright_white + "removal." + sisyphus.getclr.reset + "\n") + while True: + user_input = input(sisyphus.getclr.bright_white + "Would you like to proceed?" + sisyphus.getclr.reset + " " + + "[" + sisyphus.getclr.bright_green + "Yes" + sisyphus.getclr.reset + "/" + sisyphus.getclr.bright_red + "No" + sisyphus.getclr.reset + "]" + " ") + if user_input.lower() in ['yes', 'y', '']: + p_exe = subprocess.Popen( + ['emerge'] + args + list(pkgname)) + try: + set_nonblocking(sys.stdout.fileno()) + spinner_animation() + + sel = selectors.DefaultSelector() + sel.register(sys.stdin, selectors.EVENT_READ) + + while True: + events = sel.select(timeout=0.1) + for key, mask in events: + if key.fileobj == sys.stdin: + line = sys.stdin.readline().strip() + if line.lower() == 'q': + sys.exit() + if p_exe.poll() is not None: + break + except KeyboardInterrupt: + p_exe.terminate() + try: + p_exe.wait(1) + except subprocess.TimeoutExpired: + p_exe.kill() + sys.exit() + finally: + p_exe.wait() + sisyphus.syncdb.lcl_tbl() + break + elif user_input.lower() in ['no', 'n']: + break + else: + print("\nSorry, response" + " " + "'" + + user_input + "'" + " " + "not understood.\n") + continue |