253 lines
8.2 KiB
Python
Executable file
253 lines
8.2 KiB
Python
Executable file
#!/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()
|