diff --git a/README.md b/README.md index 80e6c59..6dc3729 100644 --- a/README.md +++ b/README.md @@ -10,24 +10,11 @@ ## Examples -🚧 Under construction 🚧 - -## Development Roadmap - -- [x] v0.1 yaml processing, args processing - - [x] config file - - [x] args processing -- [x] v0.2 basic feature complete - - [x] `info` show info - - [x] `mount` mounts the chroot - - [x] `unmount` unmount the chroot - - [x] `login` enter shell for the chroot -- [ ] v0.3 advanced features - - [x] PyPI file structure - - [x] Install default config file when not found - - [x] distro specific settings - - [x] `update` update software on target chroot - - [ ] `launch` mount chroot, launch program, and unmount after program closed in one go - - [x] `list` lists available chroots - - [x] Colorful output and debug info - - [ ] `install` download and install chroots from presets +[![asciicast](https://asciinema.org/a/FdD1pQQ3XlXO5TnafexJe1C8p.svg)](https://asciinema.org/a/FdD1pQQ3XlXO5TnafexJe1C8p) + +## Installing + +```bash +pip install ChrootMan +chrootman -h +``` diff --git a/pyproject.toml b/pyproject.toml index 85a5453..a33b7c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ChrootMan" -version = "v0.3.2" +version = "v0.3.3" description = "Cli application to manage Chroots" readme = "README.md" requires-python = ">=3.7" diff --git a/src/ChrootMan/actions.py b/src/ChrootMan/actions.py index 99296c6..0bbd98b 100644 --- a/src/ChrootMan/actions.py +++ b/src/ChrootMan/actions.py @@ -3,8 +3,8 @@ chkMountStatus, findLocation, getChrootCommand, + ensureMount, suRunCommand, - cliAskChoice, ) import logging @@ -18,7 +18,7 @@ def list(config_data, args): logging.debug(f"Arg is not used {args}") print(f"{colored('Available','green')} {colored('chroots', 'yellow')}:") for item in config_data["chroots"]: - print(colored(item, 'yellow')) + print(colored(item, "yellow")) # Show info of specified chroot @@ -26,11 +26,17 @@ def showinfo(config_data, args): chroot_name = args["chroot_name"] print(f"{colored('Name','green')}: \t\t{colored(chroot_name, 'yellow')}") try: - print(f"{colored('Description', 'green')}: \t{colored(config_data['chroots'][chroot_name]['description'], 'yellow')}") + print( + f"{colored('Description', 'green')}: \t{colored(config_data['chroots'][chroot_name]['description'], 'yellow')}" + ) except: logging.warn("Description not found, skipping") - print(f"{colored('Location', 'green')}: \t{colored(findLocation(config_data, chroot_name), 'yellow')}") - print(f"{colored('Distro', 'green')}: \t{colored(config_data['chroots'][chroot_name]['distro'], 'yellow')}") + print( + f"{colored('Location', 'green')}: \t{colored(findLocation(config_data, chroot_name), 'yellow')}" + ) + print( + f"{colored('Distro', 'green')}: \t{colored(config_data['chroots'][chroot_name]['distro'], 'yellow')}" + ) # Execute mount-command from settings @@ -44,7 +50,7 @@ def mount(config_data, args, chroot_name=None): distro = config_data["chroots"][chroot_name]["distro"] # check whether to use default or not - mount_command = getChrootCommand(config_data, distro, chroot_name, "mount-command") + mount_command = getChrootCommand(config_data, "mount-command", distro, chroot_name) suRunCommand(config_data, chroot_name, su_provider, mount_command, "mount_command") @@ -53,8 +59,15 @@ def unmount(config_data, args): su_provider = config_data["general"]["su-provider"] distro = config_data["chroots"][chroot_name]["distro"] + if not chkMountStatus(config_data, chroot_name): + logging.error(f"{chroot_name} is not mounted.") + exit(1) + unmount_command = getChrootCommand( - config_data, distro, chroot_name, "unmount-command" + config_data, + "unmount-command", + distro, + chroot_name, ) suRunCommand( config_data, chroot_name, su_provider, unmount_command, "unmount_command" @@ -63,33 +76,43 @@ def unmount(config_data, args): def login(config_data, args): chroot_name = args["chroot_name"] - if not chkMountStatus(config_data, chroot_name): - logging.error("Filesystem is not mounted! Do you want to mount it first?") - if cliAskChoice(): - mount(config_data, args) - login(config_data, args) - return + + if ensureMount(config_data, chroot_name, args, mount, login): + return su_provider = config_data["general"]["su-provider"] distro = config_data["chroots"][chroot_name]["distro"] - login_command = getChrootCommand(config_data, distro, chroot_name, "login-command") - suRunCommand(config_data, chroot_name, su_provider, login_command, "login_command") + login_command = getChrootCommand(config_data, "login-command", distro, chroot_name) + suRunCommand(config_data, chroot_name, su_provider, login_command, "login-command") + + +def launch(config_data, args): + chroot_name = args["chroot_name"] + if ensureMount(config_data, chroot_name, args, mount, launch): + return -def updateChroot(config_data, args, chroot_name): + su_provider = config_data["general"]["su-provider"] + distro = config_data["chroots"][chroot_name]["distro"] + launch_command = getChrootCommand( + config_data, "launch-command", distro, chroot_name + ) + suRunCommand(config_data, chroot_name, su_provider, launch_command, "login-command") + + unmount(config_data, args) + + +def updateChroot(config_data, args): + chroot_name = args["chroot_name"] su_provider = config_data["general"]["su-provider"] distro = config_data["chroots"][chroot_name]["distro"] update_command = getChrootCommand( - config_data, distro, chroot_name, "update-command" + config_data, "update-command", distro, chroot_name ) - if not chkMountStatus(config_data, chroot_name): - logging.error("Filesystem is not mounted! Do you want to mount it first?") - if cliAskChoice(): - mount(config_data, args, chroot_name=chroot_name) - update(config_data, args) - return + if ensureMount(config_data, chroot_name, args, mount, updateChroot): + return suRunCommand( config_data, chroot_name, su_provider, update_command, "update_command" @@ -100,13 +123,15 @@ def update(config_data, args): if not args["chroot_name"]: logging.debug("chroot_name arg is not found") if not args["all"]: - print(f"{colored('chroot_name', 'yellow')} not specified, assuming {colored('--all', 'green')}") + print( + f"{colored('chroot_name', 'yellow')} not specified, assuming {colored('--all', 'green')}" + ) for chroot in config_data["chroots"]: print(f" {colored('Updating', 'green')}: {colored(chroot, 'yellow')}") - updateChroot(config_data, args, chroot) + args["chroot_name"] = chroot + updateChroot(config_data, args) else: - chroot_name = args["chroot_name"] - logging.debug(f"Updating {chroot_name}") - updateChroot(config_data, args, chroot_name) + logging.debug(f"Updating {args['chroot_name']}") + updateChroot(config_data, args) diff --git a/src/ChrootMan/files/config.yaml b/src/ChrootMan/files/config.yaml index d335deb..fffd838 100644 --- a/src/ChrootMan/files/config.yaml +++ b/src/ChrootMan/files/config.yaml @@ -48,6 +48,9 @@ chroots: distro: arch # (Optional) Description of your chroot description: Arch linux chroot for running wine softwares + launch-command: | + chroot $rootfs_location unshare su -l steam -c "DISPLAY=:0 steam -no-cef-sandbox && exit" + debian: location: debian distro: default diff --git a/src/ChrootMan/helpers.py b/src/ChrootMan/helpers.py index 4249b4d..c06465c 100644 --- a/src/ChrootMan/helpers.py +++ b/src/ChrootMan/helpers.py @@ -5,7 +5,7 @@ def cliAskChoice(): - yes = {"yes", "yep", "y", "ye"} + yes = {"yes", "yep", "y", "ye", ""} no = {"no", "nope", "n"} choice = input(f"{colored('Input', 'blue')}: ({colored('y','green')}/{colored('n', 'red')}) > ").lower() @@ -18,7 +18,7 @@ def cliAskChoice(): return False else: print(f" Please respond with '{colored('y', 'green')}' or '{colored('n', 'red')}'") - cliAskChoice() + return cliAskChoice() def findLocation(config_data, chroot_name): @@ -43,12 +43,21 @@ def validChrootName(config_data, chroot_name): return True -def getChrootCommand(config_data, distro, chroot_name, command_name): +def getChrootCommand(config_data, command_name, distro = None, chroot_name = None): + # chroots-settings -> distro-settings -> default-distro-settings + debug(f"distro: {distro}, chroot_name: {chroot_name}, command_name: {command_name}") try: - command = config_data["general"]["distro-settings"][distro][command_name] + command = config_data["chroots"][chroot_name][command_name] except KeyError: - debug("distro is not in configured list, using default") - command = config_data["general"]["distro-settings"]["default"][command_name] + try: + command = config_data["general"]["distro-settings"][distro][command_name] + except KeyError: + try: + debug("distro is not in configured list, using default") + command = config_data["general"]["distro-settings"]["default"][command_name] + except KeyError: + error("KeyError: Make sure you have properly configured the chroots") + exit(1) command = command.replace( "$rootfs_location", findLocation(config_data, chroot_name) @@ -58,10 +67,19 @@ def getChrootCommand(config_data, distro, chroot_name, command_name): return command +def ensureMount(config_data, chroot_name, args, mount_func, command_func): + if not chkMountStatus(config_data, chroot_name): + error("Filesystem is not mounted! Do you want to mount it first?") + if cliAskChoice(): + mount_func(config_data, args) + command_func(config_data, args) + return True + return False + + def chkMountStatus(config_data, chroot_name): chrootPath = findLocation(config_data, chroot_name).replace("~", environ["HOME"]) output = subprocess.run(["mount"], capture_output=True).stdout - debug(f"Path is: {chrootPath}, mountpoints are: {str(output)}") if chrootPath in str(output): return True else: diff --git a/src/ChrootMan/parsers.py b/src/ChrootMan/parsers.py index 4e0cf11..dcc2618 100644 --- a/src/ChrootMan/parsers.py +++ b/src/ChrootMan/parsers.py @@ -69,6 +69,14 @@ def add_subcommands(subparsers): ) login_parser.set_defaults(func=login) + launch_parser = subparsers.add_parser( + "launch", help="launch chroot using command specified in chroot" + ) + launch_parser.add_argument( + "chroot_name", type=str, help="specify name of chroot to launch" + ) + launch_parser.set_defaults(func=launch) + update_parser = subparsers.add_parser("update", help="update to specified chroot") update_parser.add_argument( "chroot_name",