#!/usr/bin/python3 import os import yaml import click import subprocess from urllib.request import urlopen class colors: reset = '\033[0m' bold = '\033[01m' disable = '\033[02m' underline = '\033[04m' reverse = '\033[07m' strikethrough = '\033[09m' invisible = '\033[08m' class fg: black = '\033[30m' red = '\033[31m' green = '\033[32m' orange = '\033[33m' blue = '\033[34m' purple = '\033[35m' cyan = '\033[36m' lightgrey = '\033[37m' darkgrey = '\033[90m' lightred = '\033[91m' lightgreen = '\033[92m' yellow = '\033[93m' lightblue = '\033[94m' pink = '\033[95m' lightcyan = '\033[96m' rainbow = [lightred, orange, yellow, lightgreen, lightcyan, blue, purple] seq = 0 def random(self): if self.seq == 7: self.seq = 0 self.seq += 1 return self.rainbow[self.seq - 1] def clear_seq(self): self.seq = 0 class bg: black = '\033[40m' red = '\033[41m' green = '\033[42m' orange = '\033[43m' blue = '\033[44m' purple = '\033[45m' cyan = '\033[46m' lightgrey = '\033[47m' fg = colors.fg() def info(msg): print(colors.bold + fg.cyan + '[INFO] ' + colors.reset + msg + colors.reset) def print_list(msg): print(colors.bold + fg.random() + '[LIST] ' + colors.reset + msg + colors.reset) def modrun(msg): print(colors.bold + fg.green + '[MODRUN] ' + colors.reset + msg + colors.reset) def container_msg(msg): print(colors.bold + fg.purple + '[CONTAINER] ' + colors.reset + msg + colors.reset) def association_msg(msg): print(colors.bold + fg.random() + '[ASSOCIATION] ' + colors.reset + msg + colors.reset) def warn(warning): print(colors.bold + fg.yellow + '[WARNING] ' + colors.reset + warning + colors.reset) def error(err): print(colors.bold + fg.red + '[ERROR] ' + colors.reset + err + colors.reset) def proceed(): print(colors.bold + fg.red + '[QUESTION] ' + colors.reset + 'would you like to proceed?' + colors.reset) info(f'(press {colors.bold}ENTER{colors.reset} to proceed, or {colors.bold}^C{colors.reset}/{colors.bold}^D{colors.reset} to cancel)') input() @click.group("cli") def cli(): """Manage user operations using the user utility on blendOS.""" def main(): cli(prog_name="user") @cli.command("associate") @click.argument('association') @click.argument('container') def associate_binary(association, container): ''' Create an association (for example, apt -> ubuntu) ''' if not os.path.exists(os.path.expanduser(f'~/.local/bin/blend_bin/{association}.{container}')): error(f'{colors.bold}{association}.{container}{colors.reset} does not exist') exit() if os.path.isfile(os.path.expanduser('~/.local/bin/blend_bin/.associations')): subprocess.run(['sed', '-i', f's/^{association}\\x0//g', os.path.expanduser('~/.local/bin/blend_bin/.associations')]) with open(os.path.expanduser('~/.local/bin/blend_bin/.associations'), 'a+') as f: f.write(f'{association}\0{container}\n') _exists = os.path.exists(os.path.expanduser( f'~/.local/bin/blend_bin/{association}')) subprocess.run(['ln', '-sf', f'{association}.{container}', os.path.expanduser(f'~/.local/bin/blend_bin/{association}')]) association_msg(('modified' if _exists else 'created') + f' {colors.bold}{association} -> {container}{colors.reset}') @cli.command("dissociate") @click.argument('association') def associate_binary(association): ''' Remove an association ''' if not os.path.exists(os.path.expanduser(f'~/.local/bin/blend_bin/{association}')): error(f'{colors.bold}{association}{colors.reset} does not exist') exit() if os.path.isfile(os.path.expanduser('~/.local/bin/blend_bin/.associations')): subprocess.run(['sed', '-i', f's/^{association}\\x0//g', os.path.expanduser('~/.local/bin/blend_bin/.associations')]) subprocess.run( ['rm', '-f', os.path.expanduser(f'~/.local/bin/blend_bin/{association}')]) association_msg(f'dissociated {colors.bold}{association}') @cli.command("create-container") @click.argument('container_name') @click.argument('distro', required=False) def create_container(container_name, distro): ''' Create a container ''' if subprocess.run(['podman', 'container', 'exists', container_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0: error(f'container {colors.bold}{container_name}{colors.reset} already exists') exit(1) args = ['blend', 'create-container', '-cn', container_name] # blend handles no distro being specified already if distro: args.extend(['-d', distro]) exit(subprocess.run(args).returncode) @cli.command("delete-container") @click.argument('container') def delete_container(container): ''' Delete a container ''' if subprocess.run(['podman', 'container', 'exists', container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode != 0: error(f'container {colors.bold}{container}{colors.reset} does not exist') exit(1) subprocess.run(['blend', 'remove-container', container]) @cli.command("shell") @click.argument('container') def shell(container): ''' Enter a shell inside a container ''' if subprocess.run(['podman', 'container', 'exists', container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode != 0: error(f'container {colors.bold}{container}{colors.reset} does not exist') exit(1) creation_env = os.environ.copy() creation_env['BLEND_NO_CHECK'] = 'true' subprocess.run(['blend', 'enter', '-cn', container], env=creation_env) @cli.command("exec") @click.argument('container') @click.argument('cmds', nargs=-1, required=True) def exec_c(container, cmds): ''' Run a command inside a container ''' if subprocess.run(['podman', 'container', 'exists', container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode != 0: error(f'container {colors.bold}{container}{colors.reset} does not exist') exit(1) creation_env = os.environ.copy() creation_env['BLEND_NO_CHECK'] = 'true' cmds = [ cmd.replace('\\-', '-') for cmd in cmds] subprocess.run(['blend', 'enter', '-cn', container, '--', *cmds], env=creation_env) @cli.command("install") @click.argument('container') @click.argument('pkgs', nargs=-1, required=True) def install_c(container, pkgs): ''' Install a package inside a container ''' if os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/apt.{container}')): subprocess.run([f'sudo.{container}', 'apt', 'update']) subprocess.run([f'sudo.{container}', 'apt', 'install', *pkgs]) elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/dnf.{container}')): subprocess.run([f'sudo.{container}', 'dnf', 'install', *pkgs]) elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/pacman.{container}')): subprocess.run([f'sudo.{container}', 'pacman', '-Syu', *pkgs]) else: error(f'container {colors.bold}{container}{colors.reset} does not exist') exit(1) @cli.command("remove") @click.argument('container') @click.argument('pkgs', nargs=-1, required=True) def remove_c(container, pkgs): ''' Remove a package inside a container ''' if os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/apt.{container}')): subprocess.run([f'sudo.{container}', 'apt', 'purge', *pkgs]) elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/dnf.{container}')): subprocess.run([f'sudo.{container}', 'dnf', 'remove', *pkgs]) elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/pacman.{container}')): subprocess.run([f'sudo.{container}', 'pacman', '-Rcns', *pkgs]) else: error(f'container {colors.bold}{container}{colors.reset} does not exist') exit(1) if __name__ == '__main__': main()