diff --git a/iam/app.py b/iam/app.py index dc34a4b..f3a1358 100644 --- a/iam/app.py +++ b/iam/app.py @@ -35,14 +35,6 @@ class App: "Init application with ident" self.ident_raw = ident - - # # Split ident from scope - # scope = "" - # if "/" in ident: - # parts = ident.split("/", 2) - # ident = parts[0] - # scope = parts[1] - view = scoped_ident(ident) # Save important vars @@ -69,8 +61,11 @@ class App: else: path = os.path.join(BaseDirectory.xdg_config_home, self.name) + self.config_dir = path + if os.path.isdir(path): - files = glob.glob("*.yml", root_dir=path) + # files = glob.glob("*.yml", root_dir=path) # Python > 3.10 + files = glob.glob(f"{path}/*.yml") for file_ in files: file_ = os.path.join(path, file_) @@ -105,7 +100,6 @@ class App: providers_conf = {} providers_conf.update(self.plugin_providers) providers_conf.update(db.get("providers", {})) - # pprint (providers_conf) self.providers = Providers("ConfigProviders", providers_conf) # Load idents @@ -159,20 +153,6 @@ class App: return ret - # def get_resource(self, name): - # "Query and resolve a resource" - - # return self.catalog.get_resource(name) - - # # def get_service(self, name, resolve=False): - # # "Query and resolve a service" - - # # return self.catalog.get_service(name, resolve=resolve) - - # def list_resources(self): - # "List all resources" - # return self.catalog.resources - def list_services(self): "List all services" @@ -190,17 +170,27 @@ class App: return self.catalog.shell_disable(**kwargs) - def shell_install(self, shell="bash"): - "Install iam in shell" + def shell_install(self, shell="bash", completion=True): + "Show your install script for shell" assets_dir = os.path.join(get_root_pkg_dir("iam"), "assets") - - if shell == "bash": + # Shell selector + if shell.endswith("bash"): config_file = ".bashrc" - template_file = os.path.join(assets_dir, "init_bash.sh") + init_file = os.path.join(assets_dir, "init_bash.sh") + completion_file = os.path.join(assets_dir, "iam_completion.sh") else: raise Exception(f"Unsupported shell: {shell}") - return f"source {template_file}" \ No newline at end of file + # TOFIX: Implement for other shells ? + out = [] + out.append(f"### IAM ###") + out.append(f"if command -v iam &>/dev/null; then") + out.append(f" source {init_file}") + if completion: + out.append(f" source {completion_file}") + out.append(f"fi") + out.append(f"### IAM ###") + return '\n'.join(out) \ No newline at end of file diff --git a/iam/assets/iam_completion.sh b/iam/assets/iam_completion.sh new file mode 100644 index 0000000..1c9e971 --- /dev/null +++ b/iam/assets/iam_completion.sh @@ -0,0 +1,29 @@ +_iam_completion() { + local IFS=$'\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _IAM_COMPLETE=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +_iam_completion_setup() { + complete -o nosort -F _iam_completion iam +} + +_iam_completion_setup; + diff --git a/iam/assets/init_bash.sh b/iam/assets/init_bash.sh index 1456f06..02a8347 100644 --- a/iam/assets/init_bash.sh +++ b/iam/assets/init_bash.sh @@ -16,7 +16,7 @@ IAM_BIN=$(command -v iam) IAM_IDENT= -_usage () +i_usage () { echo "iam shell wrapper" @@ -156,7 +156,7 @@ i () #echo $IAM_BIN shell status $current_ident ;; help) - _usage + i_usage ;; esac diff --git a/iam/catalog.py b/iam/catalog.py index 508fa4f..d149094 100644 --- a/iam/catalog.py +++ b/iam/catalog.py @@ -1,12 +1,18 @@ + +import os import logging from collections import namedtuple from pprint import pprint from types import SimpleNamespace +import sh + +# TO BE REMOVED !!!! +import click from . import exceptions as error from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra from .idents import Ident -from .lib.utils import format_render, jinja_render, uniq +from .lib.utils import format_render, jinja_render, uniq, jinja_template_vars, empty logger = logging.getLogger(__name__) cli_logger = logging.getLogger("iam.cli") @@ -52,31 +58,38 @@ class ServiceCommand(DictItem): "cmd": "", # Direct commands to launch "shell": "", # Will be run in a shell (or specific shell below) - "shell_sh": "", - "shell_bash": "", - "shell_zsh": "", - "shell_fish": "", - "shell_bash": "", + + # "shell_sh": "", + # "shell_bash": "", + # "shell_zsh": "", + # "shell_fish": "", + # "shell_bash": "", - "source": False, # True for enable commands ! + # "source_output": False, # True for shell commands by default ! } # Overrides # --------------- - def payload_transform(self, name, kwargs=None): + def init(self): "Transform short form into long form" self.service = self.parent - payload = self._payload - if not isinstance(payload, dict): - payload = {"cmd": payload} - self._payload = payload + + # def payload_transform(self, name, kwargs=None): + # "Transform short form into long form" + # self.service = self.parent + + # payload = self._payload + # if not isinstance(payload, dict): + # payload = {"cmd": payload} + # self._payload = payload def cmd_name(self): return self.name.replace("_", " ") + class Service(DictItem): "Hold provider services" @@ -772,9 +785,16 @@ class Context(DictItem): assert isinstance(self.ident, Ident), f"Gotr: {self.ident}" def init(self): + self.catalog = self.parent + + root_dir = self.catalog.app.config_dir self._vars = { "ident": self.ident.name, "user": self.ident.name, + + "config_dir": root_dir, + "scripts_dir": os.path.join(root_dir, "scripts"), + "bin_dir": os.path.join(root_dir, "bin"), } def get_vars(self): @@ -790,16 +810,16 @@ class Context(DictItem): class Catalog: "Manage catalog resources" - def __init__(self, mgr, ident): + def __init__(self, app, ident): # Prepare catalog - self.mgr = mgr + self.app = app self.ident = ident # Prepare context ctx_conf = { "ident": ident, } - self.context = Context("current_context", ctx_conf) + self.context = Context("current_context", ctx_conf, parent=self) self._prepare_catalog() @@ -807,7 +827,7 @@ class Catalog: # ================ def _merge_inst_configs(self, merge=True): - plugins_confs = self.mgr.providers.get_resource_configs() + plugins_confs = self.app.providers.get_resource_configs() user_confs = self.ident.get_resource_configs() final_config = {} @@ -840,7 +860,7 @@ class Catalog: "Prepare catalog resources configs" # Get resources from providers and user - providers = self.mgr.providers + providers = self.app.providers ident = self.ident # Build kind catalog @@ -949,8 +969,8 @@ class Catalog: continue # Build loop with at least ONE item - cmd = command.cmd - if not cmd: + cmd_shell = command.shell + if not cmd_shell: continue # Create context var dict @@ -972,7 +992,7 @@ class Catalog: logger.debug(f"{log_action_name} service: {res.name}") # pprint (ctx_vars) - cmd = jinja_render(cmd, ctx_vars) + cmd = jinja_render(cmd_shell, ctx_vars) output_code.append(f"# Loading: {action_name} {res.name} ({service.name})") output_code.append(f"# =====================") output_code.append(cmd) @@ -989,15 +1009,13 @@ class Catalog: cmd, args = self.services.get_svc_command(cmd) - - # pprint (cmd.cmd) - pprint (args) + # pprint (args) service = cmd.service - pprint (service) - pprint (service.__dict__) + # pprint (service) + # pprint (service.__dict__) # pprint (dir(service)) res = service.get_linked_resource() # pprint (res) @@ -1020,25 +1038,82 @@ class Catalog: # } # ) - pprint (ctx_vars) + # pprint (ctx_vars) + + new_env = dict(os.environ) + new_env.update(ctx_vars) + # pprint(new_env) + + if cmd.cmd and cmd.shell: + logger.warning(f"Duplicate cmd and shell for {service.name}") + + out = None + - if cmd.cmd: + if cmd.shell: + mode = "shell" + payload = cmd.shell + + # Scan for missing vars !!! + tmp = jinja_template_vars(payload) + prompt_vars = [] + for var_name in tmp: + if var_name not in ctx_vars: + prompt_vars.append(var_name) + else: + var_value = ctx_vars[var_name] + if empty(var_value): + prompt_vars.append(var_name) + + # Ask for missing vars + if len(prompt_vars) > 0: + # print ("MISSING VARS") + # pprint (prompt_vars) + + answered_items = {} + for missed in prompt_vars: + default = ctx_vars.get(missed, None) + result = click.prompt(f'Select value for {missed}', default=default) + answered_items[missed] = result + + ctx_vars.update(answered_items) + + real_cmd = jinja_render(payload, ctx_vars) + + # print ("RUN SHELL") + builtins = sh.bash.bake("-c") + out = builtins(real_cmd, _fg=True, _env=new_env) + + # print (out) + + elif cmd.cmd: mode = "cmd" payload = cmd.cmd real_cmd = jinja_render(payload, ctx_vars) + cmd_parts = real_cmd.split(" ", 1) + sh_cmd = cmd_parts[0] + sh_args = cmd_parts[1] if len(cmd_parts) > 0 else [] - elif cmd.shell: - mode = "shell" - payload = cmd.shell - real_cmd = jinja_render(payload, ctx_vars) + # print ("Prepare", sh_cmd, " ||| ", sh_args) + # proc = sh.bake(sh_cmd) + # sh(sh_cmd, *sh_args, _fg=True) + + cmd = sh.Command(sh_cmd) + if sh_args: + out = cmd(sh_args, _fg=True, _env=new_env) + else: + out = cmd( _fg=True, _env=new_env) + + # print (out) else: raise Exception("MIssing cmd or shell in config !") + return out - print ("RUN CMD", mode, "\n\n\n====\n", real_cmd) \ No newline at end of file + # print ("RUN CMD", mode, "\n\n\n====\n", real_cmd) \ No newline at end of file diff --git a/iam/cli_click.py b/iam/cli_click.py index fb2e573..d657668 100755 --- a/iam/cli_click.py +++ b/iam/cli_click.py @@ -100,6 +100,7 @@ DEBUG = os.environ.get("IAM_DEBUG", "False") DEFAULT_IDENT = os.environ.get("IAM_IDENT", os.environ.get("SHELL_IDENT", "")) DEFAULT_FORMAT = os.environ.get("IAM_FORMAT", "table") DEFAULT_HELP_OPTIONS=["-h", "--help"] +DEFAULT_SHELL = os.environ.get("SHELL", "bash") # list_view.formats_enum @@ -849,7 +850,7 @@ class TmpGroup(click.Group): @click.pass_context def cli_app_run(ctx, cmd_params): - print("Will run cmd:", cmd_params) + # print("Will run cmd:", cmd_params) # Check first help parameter only show_help = False @@ -891,7 +892,8 @@ def cli_app_run(ctx, cmd_params): # Run the output ret = app.catalog.run_svc_cmd(cmd=cmd_params) - pprint (ret, expand_all=True) + print (ret) + # pprint (ret, expand_all=True) @@ -910,11 +912,14 @@ def cli_app_run(ctx, cmd_params): @cli__shell.command("install") +@click.argument("shell", + required=False, + default=DEFAULT_SHELL) @click.pass_context -def shell_install(ctx, verbose=None): +def shell_install(ctx, shell=None, verbose=None): "Install iam in your shell" - ret = ctx.obj.app.shell_install() + ret = ctx.obj.app.shell_install(shell=shell) # print("-- %< --" * 8) print(ret) diff --git a/iam/lib/click_utils.py b/iam/lib/click_utils.py index 1f4d8ad..5b01395 100644 --- a/iam/lib/click_utils.py +++ b/iam/lib/click_utils.py @@ -1,5 +1,9 @@ +import logging import click +logger = logging.getLogger(__name__) + + # Click Plugins # =============================== @@ -9,8 +13,53 @@ class NestedHelpGroup(click.Group): """This class provides a way to show all commands of all children instead of just the parent. Optionnaly accept to hides groups or not. + This class is aimed to serve simple resource based CLIs, à la [cliff](), + but with the click library. + + This class provides: + * Recursive command listing + * Command aliasing as described in (documentation)[https://click.palletsprojects.com/en/8.1.x/advanced/#command-aliases] + * Rich support (TODO, more complete wrapper of click-rich) + * Basic views (TODO) + + # Recursive command list + # ============= + Recursive listing on help commands, this is helpful to + have an quick overview of all commands on small clis. It also provides + an option to hide groups if you don't use them, which may reduce the size of + the help message in case of many groups. + + + # Command shortcuts + # ============= + Let's image a command line that provides: + ``` + myapp user show + myapp user state + myapp user disable + ``` + You could use as shortcuts: + ``` + mysapp u sh USER + mysapp u st USER + mysapp u sd USER + ``` + But `mysapp u s USER` would fail as it would not know if it have to redirect to + `show` or `state` command, as both starts with `s` + """ + + # For partial name resolution + def resolve_command(self, ctx, args): + "Return the full command name if changed" + + _, cmd, args = super().resolve_command(ctx, args) + if _ != cmd.name: + logger.debug(f"Rewrite command '{_}' to '{cmd.name}'") + return cmd.name, cmd, args + + def get_command(self, ctx, cmd_name: str): """Given a context and a command name, this returns a :class:`Command` object if it exists or returns ``None``. @@ -18,19 +67,37 @@ class NestedHelpGroup(click.Group): # Resolve name part by part parts = cmd_name.split(" ") + len_parts = len(parts) -1 curr = self for idx, part in enumerate(parts): - curr = curr.commands.get(part) + match = curr.commands.get(part) + + # Look for shortcut if last part + if match is None: + # Look for direct children only + matches = [x for x in self._resolve_children(self, ctx=ctx, ignore_groups=False, deep=0) + if x.startswith(cmd_name)] + + # Look for possible matches + if not matches: + pass + elif len(matches) == 1: + match = click.Group.get_command(self, ctx, matches[0]) + else: + ctx.fail(f"Too many matches for {cmd_name}: {', '.join(sorted(matches))}") + + # Iterate over next child! + curr = match return curr def list_commands(self, ctx) -> list[str]: "List all children commands" - return self._resolve_children(self, ctx=ctx, ignore_groups=True) + return sorted(self._resolve_children(self, ctx=ctx, ignore_groups=True)) @classmethod - def _resolve_children(cls, obj, ctx=None, ignore_groups=False, _parent=None): + def _resolve_children(cls, obj, ctx=None, ignore_groups=False, _parent=None, deep=-1): "Resolve recursively all children" # Source: Adapted from https://stackoverflow.com/a/56159096 @@ -51,12 +118,15 @@ class NestedHelpGroup(click.Group): ret.append(record) # Recursive loop - ret.extend( - cls._resolve_children( - child, ctx=ctx, ignore_groups=ignore_groups, _parent=full_name + if deep != 0: + deep = deep -1 + ret.extend( + cls._resolve_children( + child, ctx=ctx, ignore_groups=ignore_groups, _parent=full_name, deep=deep + ) ) - ) return ret return [] + diff --git a/iam/lib/utils.py b/iam/lib/utils.py index 554ef81..d9fec06 100644 --- a/iam/lib/utils.py +++ b/iam/lib/utils.py @@ -83,6 +83,20 @@ def prune(items): raise Exception(f"Function prune requires a list or a dict, got: {items}") return ret +# from jinja2 import Environment, FileSystemLoader, meta + +# env = Environment(loader=FileSystemLoader('templates')) +# parsed_content = env.parse(payload) + + +from jinja2 import Environment, PackageLoader, meta + +def jinja_template_vars(payload): + + env = Environment() #loader=PackageLoader('templates')) + parsed_content = env.parse(payload) + return meta.find_undeclared_variables(parsed_content) + def jinja_render(payload, vars): "Parse a string with jinja" diff --git a/iam/plugins/base.py b/iam/plugins/base.py index 643f88c..08f51fe 100644 --- a/iam/plugins/base.py +++ b/iam/plugins/base.py @@ -43,19 +43,19 @@ plugin_base = { "commands": { "shell_enable": { "desc": "Enable shell ident", - "cmd": "export SHELL_IDENT={{ident}}", + "shell": "export SHELL_IDENT={{ident}}", }, "shell_disable": { "desc": "Disable shell ident", - "cmd": "unset SHELL_IDENT", + "shell": "unset SHELL_IDENT", }, "id new": { "desc": "Create shell identy", - "cmd": "add_ident {{ param }}", + "shell": "add_ident {{ param }}", }, "id delete": { "desc": "Delete shell identy", - "cmd": "rm_ident {{ param }}", + "shell": "rm_ident {{ param }}", }, }, }, diff --git a/iam/plugins/devops.yml b/iam/plugins/devops.yml index a6d8f7a..2b82076 100644 --- a/iam/plugins/devops.yml +++ b/iam/plugins/devops.yml @@ -17,13 +17,13 @@ providers: shell_enable: desc: Enable git identity - cmd: | + shell: | export GH_TOKEN='{{gh_token}}' export GH_REPO='{{ident}}' shell_disable: desc: Disable git identity - cmd: | + shell: | unset GH_TOKEN GH_REPO @@ -69,13 +69,13 @@ providers: shell_enable: desc: Enable git identity - cmd: | + shell: | export GITEA_SERVER_URL='{{gitea_server_url}}' export GITEA_LOGIN='{{email}}' shell_disable: desc: Disable git identity - cmd: | + shell: | unset GITEA_SERVER_URL GITEA_LOGIN @@ -120,7 +120,7 @@ providers: shell_enable: desc: Enable minio alias - cmd: | + shell: | export MINIO_ACCESS_KEY={{minio_access}} export MINIO_SECRET_KEY={{minio_secret}} # cmd_FUTURE: | @@ -130,18 +130,18 @@ providers: shell_disable: desc: Disable minio alias - cmd: | + shell: | unset MINIO_ACCESS_KEY MINIO_SECRET_KEY minio new alias: desc: Create alias - cmd: | + shell: | mc alias set {{ident}} {{api_url}} {{minio_user}} {{minio_password}} minio delete alias: desc: Remove alias - cmd: | + shell: | mc alias rm {{ident}} diff --git a/iam/plugins/local.py b/iam/plugins/local.py index 9ca7922..f4483cc 100644 --- a/iam/plugins/local.py +++ b/iam/plugins/local.py @@ -5,174 +5,8 @@ from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml yml_dir = get_pkg_dir(__name__) plugin_conf = open_yaml(os.path.join(yml_dir, "local.yml"))[0] + + + + all = plugin_conf.get("providers", {}) - - -# plugin_ssh = { - -# "services": { - -# "local.ssh_key": { -# "desc": "Local ssh key", -# # "input": { -# # "ssh_agent_socket_dir": "/run/user/ssh-agent", -# # "ssh_agent_tmout": "7d", -# # }, -# "commands": { -# "shell_enable": { -# "cmd": """ -# export SSH_AUTH_SOCK={{ssh_agent_socket_dir}}/{{user}} && \ -# ssh-agent -a $SSH_AUTH_SOCK -t {{ssh_agent_tmout}} -# """, -# }, -# "shell_disable": { -# "cmd": "ssh-agent -k && unset SSH_AUTH_SOCK", -# }, -# }, -# }, -# }, - - -# "resources_def": { -# "service.local.ssh_key": { -# "desc": "A local ssh key", -# }, - -# "auth.ssh_certificate": { -# "desc": "SSH Certificates", -# "input": {"ssh_cert_file": None}, -# "needs": ["auth.ssh_key"], -# }, - -# "auth.ssh_key": { -# "desc": "ssh_key", -# "input": { -# "ssh_key_file": None, -# "ssh_key_secret": None -# }, -# "needs": [ -# {"kind": "auth.password", "remap": {"ssh_key_secret": "passord"}} -# ], -# }, - -# "account.ssh": {"desc": "An unix account", "input": {"host": None}}, - - -# }, - -# "resources": { - -# "service.local.ssh_agent": { -# "enabled": True, -# # "contains": [ -# # "auth.ssh_key:{ident}/ed25519", -# # "auth.ssh_key:{ident}/rsa4096", -# # "auth.ssh_key:{ident}/rsa2048", -# # "auth.ssh_key:{ident}/rsa1024", -# # "auth.ssh_key:{ident}", -# # ], -# }, - -# "service.local.ssh_agent_keys": { -# "enabled": True, -# "loop_limit": 3, -# "loop": [ -# "auth.ssh_key:{ident}/ed25519", -# "auth.ssh_key:{ident}/rsa4096", -# "auth.ssh_key:{ident}/rsa2048", -# "auth.ssh_key:{ident}/rsa1024", -# "auth.ssh_key:{ident}", -# ], -# }, - -# }, - -# } - - -# plugin_ssh_agent = { -# "services": { - -# "local.ssh_agent": { -# "desc": "Local ssh-agent", -# "input": { -# "ssh_agent_socket_dir": "/run/user/ssh-agent", -# "ssh_agent_tmout": "7d", -# }, -# "commands": { -# "shell_enable": { -# "cmd": """ -# export SSH_AUTH_SOCK={{ssh_agent_socket_dir}}/{{user}} && \ -# ssh-agent -a $SSH_AUTH_SOCK -t {{ssh_agent_tmout}} -# """, -# }, -# "shell_disable": { -# "cmd": "ssh-agent -k && unset SSH_AUTH_SOCK", -# }, -# }, -# }, - -# "local.ssh_agent_keys": { -# "desc": "Local ssh-agent keys", -# "required_services": [ -# "local.ssh_agent" -# ], -# "commands": { -# "shell_enable": { -# "cmd": """ -# ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %} -# """, -# }, -# "shell_disable": { -# "cmd": "ssh-agent -d {ssh_key_file}", -# }, -# }, -# }, - -# }, - -# "resources_def": { -# "service.local.ssh_agent": { -# "desc": "A local ssh_agent service", -# }, -# "service.local.ssh_agent_keys": { -# "desc": "A local ssh_agent_keys service", -# }, -# }, - -# "resources": { - -# "service.local.ssh_agent": { -# "enabled": True, -# # "contains": [ -# # "auth.ssh_key:{ident}/ed25519", -# # "auth.ssh_key:{ident}/rsa4096", -# # "auth.ssh_key:{ident}/rsa2048", -# # "auth.ssh_key:{ident}/rsa1024", -# # "auth.ssh_key:{ident}", -# # ], -# }, - -# "service.local.ssh_agent_keys": { -# "enabled": True, -# "loop_limit": 3, -# "loop": [ -# "auth.ssh_key:{ident}/ed25519", -# "auth.ssh_key:{ident}/rsa4096", -# "auth.ssh_key:{ident}/rsa2048", -# "auth.ssh_key:{ident}/rsa1024", -# "auth.ssh_key:{ident}", -# ], -# }, - -# }, -# } - - -# all = { -# "ssh": plugin_ssh, -# "ssh_agent": plugin_ssh_agent, -# } - - -# print (to_yaml(all)) diff --git a/iam/plugins/local.yml b/iam/plugins/local.yml index add62e0..af70a80 100644 --- a/iam/plugins/local.yml +++ b/iam/plugins/local.yml @@ -17,18 +17,32 @@ providers: ssh new: desc: Create new SSH key + + # shell: | + # echo "This is my shelll !!! $SHELL" + # env | grep jez + + # cmd: | + # env + # # echo Create ssh key: {{ssh_key_alg}} for ident '{{ident}}' with pass: {{ssh_key_secret}} + shell: | + # env | sort + # echo + + SSH_KEY_ALG={{ssh_key_alg}} SSH_KEY_VERSION="$(date +'%Y%m%d')" SSH_KEY_HOST="$(hostname -f)" - SSH_KEY_FILE=$HOME/.ssh/{ident}/{user}_${SSH_KEY_ALG}_${SSH_KEY_VERSION} - SSH_KEY_COMMENT={user}@${SSH_KEY_HOST}:${SSH_KEY_ALG}_${SSH_KEY_VERSION} + SSH_KEY_FILE=$HOME/.ssh/{{ident}}/{{user}}_${SSH_KEY_ALG}_${SSH_KEY_VERSION} + SSH_KEY_COMMENT={{user}}@${SSH_KEY_HOST}:${SSH_KEY_ALG}_${SSH_KEY_VERSION} - ssh-keygen -f "{SSH_KEY_FILE}" \ + echo mkdir -p $HOME/.ssh/{{ident}}/ + echo ssh-keygen -f "${SSH_KEY_FILE}" \ -t ed25519 -a 100 \ - -N "{{ssh_key_secret}}" \ + {% if ssh_key_secret %}-N "{{ssh_key_secret}}"{%endif%} \ -C "$SSH_KEY_COMMENT" @@ -36,7 +50,7 @@ providers: ssh delete: desc: Delete existing SSH key cmd: | - find $HOME/.ssh/{ident}/ -name "{user}_*" + find $HOME/.ssh/{{ident}}/ -name "{{user}}_*" @@ -124,7 +138,7 @@ providers: shell_start: desc: Start ssh-agent - cmd: | + shell: | socket=$HOME/.local/state/ssh-agent/{{user}} start=true @@ -154,7 +168,7 @@ providers: shell_enable: desc: Enable ssh-agent - cmd: | + shell: | socket=$HOME/.local/state/ssh-agent/{{user}} if [[ -e "$socket.env" ]]; then @@ -168,13 +182,13 @@ providers: shell_disable: desc: Disable ssh-agent - cmd: | + shell: | unset SSH_AUTH_SOCK SSH_AGENT_PID shell_stop: desc: Kill ssh-agent - cmd: | + shell: | socket=$HOME/.local/state/ssh-agent/{{user}} if [[ -e "$socket.env" ]]; then @@ -205,11 +219,11 @@ providers: commands: ssh add: desc: Unload keys into ssh-agent - cmd: ssh-agent -d {ssh_key_file} + shell: ssh-agent -d {ssh_key_file} ssh rm: desc: Load keys into ssh-agent - cmd: | + shell: | ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %} @@ -261,7 +275,7 @@ providers: shell_enable: desc: Enable git identity - cmd: | + shell: | export GIT_AUTHOR_NAME='{{ident}}' export GIT_AUTHOR_EMAIL='{{email}}' export GIT_COMMITTER_NAME='{{ident}}' @@ -270,7 +284,7 @@ providers: shell_disable: desc: Disable git identity - cmd: | + shell: | unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL @@ -284,13 +298,13 @@ providers: shell_enable: desc: Enable git home management - cmd: | + shell: | export GIT_DIR="{{git_dir}}" export GIT_WORK_TREE="{{git_work_tree}}/{{ ident }}" shell_disable: desc: Disable git home management - cmd: | + shell: | unset GIT_DIR GIT_WORK_TREE required_services: @@ -331,13 +345,13 @@ providers: shell_enable: desc: Enable PS1 - cmd: | + shell: | export OLD_PS1=$PS1 export PS1="\033[0;34m\]({{ident}})\033[00m\] ${PS1}" shell_disable: desc: Disable PS1 - cmd: | + shell: | export PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' # export PS1="$OLD_PS1" diff --git a/poetry.lock b/poetry.lock index c8c98ee..432d4ba 100644 --- a/poetry.lock +++ b/poetry.lock @@ -424,6 +424,17 @@ rich = ">=10.7.0" [package.extras] dev = ["pre-commit"] +[[package]] +name = "sh" +version = "2.0.6" +description = "Python subprocess replacement" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "sh-2.0.6-py3-none-any.whl", hash = "sha256:ced8f2e081a858b66a46ace3703dec243779abbd5a1887ba7e3c34f34da70cd2"}, + {file = "sh-2.0.6.tar.gz", hash = "sha256:9b2998f313f201c777e2c0061f0b1367497097ef13388595be147e2a00bf7ba1"}, +] + [[package]] name = "typer" version = "0.9.0" @@ -459,4 +470,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "1747400a30523d50e0ccaf55386f60f05b4d6e5e222aeb8a737d4e78b702a210" +content-hash = "2673f8f06cf73701e5dac7ef463abeaf01bfb40b5c003e0898c7d97d0c589158" diff --git a/pyproject.toml b/pyproject.toml index 1c96b3b..0d465b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ coloredlogs = "^15.0.1" rich = "^13.6.0" rich-click = "^1.6.1" colorama = "^0.4.6" +sh = "^2.0.6" [tool.poetry.scripts]