update: plugin system to load yaml/python

This commit is contained in:
mrjk 2023-10-11 03:18:30 -04:00
parent cbe88fba59
commit 724eb8e780
9 changed files with 295 additions and 504 deletions

View File

@ -1,6 +1,7 @@
import glob
import logging
import os
import sys
from collections import namedtuple
from copy import copy
from pprint import pprint
@ -12,23 +13,24 @@ from . import exceptions as error
from .catalog import Catalog
from .framework import DictItem, scoped_ident
from .idents import Idents
from .lib.utils import get_root_pkg_dir, import_module, open_yaml
from .lib.utils import (filter_existing_files, get_root_pkg_dir, import_module,
open_yaml, to_yaml)
from .meta import NAME, VERSION
from .providers import Providers
logger = logging.getLogger(__name__)
IamException = error.IamException
# IamException = error.IamException
class App:
name = NAME
def __init__(self, ident="_", config_path=None):
# Load application
db = self.load_config_files(config_path=config_path)
def __init__(self, ident="_", config_path=None, cli_context=None):
self.cli_ctx = cli_context
self.load_db(db)
# Load application
self.init_db(config_path)
self.init_ident(ident)
def init_ident(self, ident):
@ -49,7 +51,7 @@ class App:
self.ident = self.idents.get(self.ident_name)
# Load user and catalog
self.catalog = Catalog(self, self.ident)
self.catalog = Catalog(self, self.ident, self.cli_ctx)
def load_config_files(self, config_path=None):
"Fetch config from default place"
@ -68,7 +70,7 @@ class App:
files = glob.glob(f"{path}/*.yml")
for file_ in files:
file_ = os.path.join(path, file_)
# file_ = os.path.join(path, file_) # Python > 3.10, see above
payloads = open_yaml(file_)
for payload in payloads:
config_candidates.append(
@ -89,16 +91,24 @@ class App:
return config
def load_db(self, db):
def init_db(self, config_path):
"Load database"
# Update python PATH for later plugin loading
self.conf_plugin_dir = os.path.join(config_path, "plugins")
sys.path.insert(0, self.conf_plugin_dir)
# Init Databases
db = self.load_config_files(config_path=config_path)
self.db = db
self._load_plugins(db.get("plugins", []))
# Load providers
providers_conf = {}
providers_conf.update(self.plugin_providers)
# pprint(providers_conf)
providers_conf.update(db.get("providers", {}))
self.providers = Providers("ConfigProviders", providers_conf)
@ -114,29 +124,52 @@ class App:
plugins = {}
for plugin_name in plugins_refs:
if not ":" in plugin_name:
plugin_name = plugin_name + ":all"
providers = self.parse_plugin_def(plugin_name)
load_one = False
if not plugin_name.endswith(":all"):
load_one = plugin_name.split(":", 2)[1]
plugins.update(providers)
mod = None
try:
mod = import_module(plugin_name)
except ModuleNotFoundError as err:
msg = f"Impossible to load module: {plugin_name}"
raise IamException(msg)
print(err)
continue
if load_one:
mod = {load_one: mod}
plugins.update(mod)
# print (to_yaml(mod))
self.plugin_providers = plugins
def parse_plugin_def(self, plugin_name):
mod_name = plugin_name
mod_comp = "__all__"
if ":" in plugin_name:
mod_name, mod_comp = plugin_name.split(":", 2)
plugin_name = f"{mod_name}:{mod_comp}"
# Load plugin
providers = None
matches = filter_existing_files(
self.conf_plugin_dir, [f"{mod_name}.yml", f"{mod_name}.yaml"]
)
if matches:
logger.debug(f"Load YAML plugin {mod_name}")
providers = open_yaml(matches[0])[0].get("providers")
else:
try:
mod = import_module(mod_name)
logger.debug(f"Load Python plugin {mod_name}")
providers = mod.providers
except ModuleNotFoundError:
pass
# pprint(providers)
# Report empty plugins
if not providers:
_msg = f"Impossible to load module: {plugin_name}"
raise error.ConfigUnknownPlugin(_msg)
# Select only required items
if not plugin_name.endswith(":__all__"):
item = providers.get(mod_comp)
providers = {mod_comp: item}
return providers
def user_dump(self, pattern=None):
"Dump ident config"

View File

@ -11,8 +11,8 @@ import sh
from . import exceptions as error
from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra
from .idents import Ident
from .lib.utils import (empty, format_render, jinja_render,
jinja_template_vars, uniq)
from .lib.utils import (duplicates, empty, format_render, import_module,
jinja_render, jinja_template_vars, uniq)
logger = logging.getLogger(__name__)
cli_logger = logging.getLogger("iam.cli")
@ -55,6 +55,7 @@ class ServiceCommand(DictItem):
default_attrs = {
"desc": "Service without description",
"cmd": "", # Direct commands to launch
"python": "", # Will be run in as python
"shell": "", # Will be run in a shell (or specific shell below)
# "shell_sh": "",
# "shell_bash": "",
@ -301,7 +302,7 @@ class Services(DictCtrl):
# Retrieve command list
cmds = self.list_cmds()
cmd_names = [cmd.name for cmd in cmds]
cmd_names = [cmd.cmd.name for cmd in cmds]
# Find best matching command
cmd_split_idx = None
@ -328,26 +329,28 @@ class Services(DictCtrl):
def list_cmds(self):
"List all services commands"
ret = []
cmd_catalog = []
for name, service in self.items():
cmds = service.get_cmds()
ret.extend(cmds)
# Check for invalid configs
conf = []
for cmd in ret:
prefix = cmd.name.split(" ")[0]
if prefix in self.RESERVED_CMD_PREFIX:
_msg = f"Forbidden prefix! {cmd}"
raise Exception(_msg)
conf.append(cmd.name)
dump = uniq(conf)
for cmd in cmds:
# Check for invalid prefixes
prefix = cmd.name.split(" ")[0]
if prefix in self.RESERVED_CMD_PREFIX:
_msg = f"Forbidden prefix for command '{cmd.name}' in service: '{service.name}'"
raise error.ConfigForbiddenPrefixCmds(_msg)
if conf != dump:
_msg = f"Duplicates values! {conf}"
raise Exception(_msg)
# Check for duplicates
dups = [x for x in cmd_catalog if x.cmd.name == cmd.name]
if dups:
svcs = ",".join([x.svc.name for x in dups])
_msg = f"Duplicate command name '{cmd.name}' for services: '{svcs}' and '{service.name}'"
raise error.ConfigDuplicateCmds(_msg)
return ret
# Append to catalog
cmd_catalog.append(SimpleNamespace(svc=service, cmd=cmd))
return cmd_catalog
def get_linked_resources(self):
"""
@ -763,6 +766,7 @@ class Context(DictItem):
default_attrs = {
"ident": None,
"cli_ctx": {},
}
# Overrides
@ -795,7 +799,7 @@ class Context(DictItem):
class Catalog:
"Manage catalog resources"
def __init__(self, app, ident):
def __init__(self, app, ident, cli_ctx=None):
# Prepare catalog
self.app = app
self.ident = ident
@ -803,6 +807,7 @@ class Catalog:
# Prepare context
ctx_conf = {
"ident": ident,
"cli_ctx": cli_ctx,
}
self.context = Context("current_context", ctx_conf, parent=self)
@ -847,9 +852,11 @@ class Catalog:
# Get resources from providers and user
providers = self.app.providers
ident = self.ident
# pprint (self.__dict__)
# Build kind catalog
res_kind_configs = providers.get_resource_kinds()
assert res_kind_configs, "TOFIX"
self.resources_kind = ResourcesKinds(
"CatalogResourcesKinds", payload=res_kind_configs, parent=self
)
@ -986,12 +993,13 @@ class Catalog:
def run_svc_cmd(self, cmd):
"Run a command against the services"
cmd, args = self.services.get_svc_command(cmd)
cmd_def, args = self.services.get_svc_command(cmd)
# pprint (cmd.cmd)
# pprint (cmd_def)
# pprint (args)
service = cmd.service
service = cmd_def.svc
cmd = cmd_def.cmd
# pprint (service)
# pprint (service.__dict__)
@ -1019,15 +1027,17 @@ class Catalog:
# pprint (ctx_vars)
env_prefix = "IAM_"
new_env = dict(os.environ)
new_env.update(ctx_vars)
# pprint(new_env)
new_env.update({env_prefix + key: val for key, val in ctx_vars.items()})
if cmd.cmd and cmd.shell:
logger.warning(f"Duplicate cmd and shell for {service.name}")
# Validate config: TOFIX: Should not be here !
tests = [cmd.cmd, cmd.shell, cmd.python]
tests = [x for x in tests if x]
if len(tests) > 1:
logger.warning(f"Duplicate cmd and shell for {service.name}: {tests}")
out = None
if cmd.shell:
mode = "shell"
payload = cmd.shell
@ -1069,9 +1079,12 @@ class Catalog:
payload = cmd.cmd
real_cmd = jinja_render(payload, ctx_vars)
cmd_parts = real_cmd.split(" ", 1)
# print ("PAYLOAD", real_cmd)
cmd_parts = real_cmd.split(" ")
sh_cmd = cmd_parts[0]
sh_args = cmd_parts[1] if len(cmd_parts) > 0 else []
sh_args = cmd_parts[1:] if len(cmd_parts) > 1 else []
sh_args.extend(args)
# print ("Prepare", sh_cmd, " ||| ", sh_args)
@ -1080,11 +1093,41 @@ class Catalog:
cmd = sh.Command(sh_cmd)
if sh_args:
# sh_args = sh_args.split(" ")
pprint(cmd)
pprint(sh_args)
out = cmd(sh_args, _fg=True, _env=new_env)
else:
out = cmd(_fg=True, _env=new_env)
# print (out)
elif cmd.python:
mode = "python"
plugin_name = cmd.python
# print ("LOAD MODULE", plugin_name)
# this expect a callable, function or class
try:
mod = import_module(plugin_name)
except ModuleNotFoundError as err:
msg = f"Impossible to load module: {plugin_name}"
raise IamException(msg)
print(err)
cmd_name = cmd.name.replace(" ", "_")
# pprint (mod)
# print ("locals")
# pprint (locals())
# print ("RUN PLUGIN")
mod(
cmd_name=cmd_name,
env=new_env,
vars=ctx_vars,
args=args,
ctx=self.context,
)
else:
raise Exception("MIssing cmd or shell in config !")

View File

@ -4,6 +4,7 @@ import io
import logging
import os
import sys
import traceback
from enum import Enum
from pprint import pprint
from types import SimpleNamespace
@ -45,7 +46,7 @@ else:
import rich_click as click
from rich_click import RichGroup
from rich import box # , print
from rich import box, print
from rich.console import Console
# Overrides defaults
from rich.pretty import pprint
@ -90,7 +91,8 @@ list_view = ViewList(output=console.print)
APP_NAME = "iam"
APP_VERSION = "__version__TOFIX"
APP_EXCEPTION = error.IamException
APP_TTY = os.isatty(0)
APP_COLORS = APP_TTY or APP_RICH
# See: https://docs.python.org/3.8/library/logging.html#logging-levels
DEFAULT_LOG_LEVEL = 30 # warning
@ -129,16 +131,37 @@ def global_options(function):
# is_flag=True, show_default=False, default=False,
# help="Force"
# )(function)
# function = click.option(
# "interactive", "--interactive", "-i"
# is_flag=True, show_default=False, default=False,
# help="Interactive mode"
# )(function)
function = click.option(
"interactive",
"--interactive/--no-interactive",
"-I",
is_flag=True,
show_default=APP_TTY,
default=APP_TTY,
help="Interactive mode",
)(function)
function = click.option(
"colors",
"--colors/--no-colors",
"-C",
is_flag=True,
show_default=APP_COLORS,
default=APP_COLORS,
help="Enable colors",
)(function)
# function = click.option(
# "recursive", "--recursive", "-r"
# is_flag=True, show_default=False, default=False,
# help="Recursive mode"
# )(function)
# function = click.option(
# 'dry_run', '-n', '--dry-run',
# is_flag=True, show_default=False, default=False,
# help="Dry run, do not take any actions."
# )(function)
function = click.option(
"quiet",
@ -171,12 +194,6 @@ def global_options(function):
def format_options(function):
# function = click.option(
# 'dry_run', '-n', '--dry-run',
# is_flag=True, show_default=False, default=False,
# help="Dry run, do not take any actions."
# )(function)
# Duplicates
function = click.option(
"-v",
@ -243,6 +260,8 @@ def cli_app(
config: str = DEFAULT_IDENT,
ident: str = DEFAULT_IDENT,
quiet=False,
interactive=False,
colors=False,
log_trace=False,
fmt_fields=None,
fmt_sort=None,
@ -255,6 +274,15 @@ def cli_app(
is not all that interesting.
"""
# Show trace on errors, more like the --trace flag
if log_trace:
global APP_EXCEPTION
APP_EXCEPTION = None
# Disable COLORS
global APP_COLORS
APP_COLORS = colors
# Calculate log level
log_level = DEFAULT_LOG_LEVEL - verbose * 10
log_level = log_level if log_level > 0 else 10
@ -272,6 +300,8 @@ def cli_app(
log_level=log_level,
fmt_name=fmt_name or DEFAULT_FORMAT,
ident=ident,
interactive=interactive,
quiet=quiet,
)
render_config = {
"fmt_name": cli_config.fmt_name,
@ -288,7 +318,7 @@ def cli_app(
# Instanciate app
logger.info(f"Current ident: {ident}")
ctx.obj = SimpleNamespace(
app=App(config_path=config, ident=ident),
app=App(config_path=config, ident=ident, cli_context=cli_config),
cli=cli_config,
render_item=render_item,
render_list=render_list,
@ -892,12 +922,13 @@ def cli_app_run(ctx, cmd_params):
if show_list:
ret = app.catalog.services.list_cmds()
data = [[x.name, x.desc] for x in ret]
pprint(ret)
data = [[x.cmd.name, x.cmd.desc, x.svc.name] for x in ret]
# Render data
ctx.obj.render_list(
data,
["name", "desc"],
["name", "desc", "service"],
conf={
"table_title": f"Services commands",
# "fmt_name": fmt_name,
@ -969,11 +1000,18 @@ def run():
clean_terminate(err)
# Developper catchall
raise Exception(err) from err
# logger.error(traceback.format_exc())
# logger.critical("Uncatched error %s; this may be a bug!", err.__class__)
# logger.critical("Exit 1 with bugs")
# sys.exit(1)
if APP_COLORS:
console.print_exception(show_locals=True)
else:
# raise Exception(err) from err
logger.error(traceback.format_exc())
if APP_EXCEPTION:
logger.critical("Uncatched error %s; this may be a bug!", err.__class__)
logger.critical("Exit 1 with bugs")
else:
logger.critical("Exited with %s error: %s", err.__class__.__name__, err)
sys.exit(1)
if __name__ == "__main__":

View File

@ -2,6 +2,10 @@ class IamException(Exception):
"Iam Exception"
# Second level classes
# =====================
class UnresolvedResourceDependencies(IamException):
"Raised when resources dependency is unmet"
@ -20,3 +24,23 @@ class MissingConfigFiles(IamException):
class UnknownServiceCommand(IamException):
"Raised when a command is not matched against services"
# Configuration errors
# =====================
class ConfigurationError(IamException):
"Raised when a command is not matched against services"
class ConfigDuplicateCmds(ConfigurationError):
"Raised when two service commands overlap. May be caused by plugins"
class ConfigForbiddenPrefixCmds(ConfigurationError):
"Raised when a service is not correctly prefixed. May be caused by plugins"
class ConfigUnknownPlugin(ConfigurationError):
"Raised when a plugin can't be found"

View File

@ -1,4 +1,5 @@
import importlib
import importlib.util
import json
import logging
import os
@ -72,6 +73,21 @@ def empty(item):
return False
def duplicates(_list):
"""Check if given list contains duplicates"""
known = set()
dup = set()
for item in _list:
if item in known:
dup.add(item)
else:
known.add(item)
if len(dup) > 0:
return list(dup)
return []
def prune(items):
"Prune empty lists, dicts and None values from list or dict"
@ -197,6 +213,16 @@ def to_hcl(data):
# return tomllib.dumps(data)
def filter_existing_files(root_path, candidates):
"""Return only existing files"""
result = [
os.path.join(root_path, cand)
for cand in candidates
if os.path.isfile(os.path.join(root_path, cand))
]
return list(set(result))
def to_toml(data):
"Render to toml"
pprint(data)
@ -274,6 +300,18 @@ def get_pkg_dir(name):
return os.path.dirname(mod.__file__)
def import_module_file(package, path):
"Simple helper to load dynamically python modules"
spec = importlib.util.spec_from_file_location(package, path)
mod = importlib.util.module_from_spec(spec)
sys.modules[package] = mod
spec.loader.exec_module(mod)
return mod
def import_module_pkg(package):
"Simple helper to load dynamically python modules"
return importlib.import_module(package)

View File

@ -1,7 +1,21 @@
plugin_base = {
"resources_def": {
# Secrets
# Main
# -------------
"secret": {"desc": "Secret", "input": {"secret": None}},
"auth": {"desc": "Authentification"},
"account": {
"desc": "Account",
"input": {"password": None, "user": None},
},
"service": {"desc": "Session service"},
"scope": {"desc": "Scoped identity"},
# Bases
"service.id": {"desc": "Default ident service"},
"service.scope": {"desc": "Default scope service"},
# Other assets:
# -------------
# Secrets
"secret.env": {
"desc": "Environment secret" "vars",
"input": {"secret_env": None},
@ -11,7 +25,6 @@ plugin_base = {
"input": {"secret_file": None},
},
# Auths
"auth": {"desc": "Authentification"},
"auth.password": {"desc": "Password", "input": {"password": None}},
"auth.token": {
"desc": "Token",
@ -22,20 +35,15 @@ plugin_base = {
"input": {"token": None},
},
# Accounts
"account": {
"desc": "Account",
"input": {"password": None, "user": None},
},
"account.email": {"desc": "Email account", "input": {"email": None}},
# Services
"service": {"desc": "Session service"},
# ID
"service.id": {"desc": "Default ident service"},
},
"resources": {
"service.id": {
"enabled": True,
},
"service.scope": {
"enabled": True,
},
},
"services": {
"id": {
@ -59,8 +67,29 @@ plugin_base = {
},
},
},
"scope": {
"desc": "Local scope identity/sub identities",
"commands": {
"shell_enable": {
"desc": "Enable shell scope",
"shell": "export SHELL_SCOPE={{scope}}",
},
"shell_disable": {
"desc": "Disable shell scope",
"shell": "unset SHELL_SCOPE",
},
"scope new": {
"desc": "Create shell scope",
"shell": "add_scope {{ param }}",
},
"scope delete": {
"desc": "Delete shell scope",
"shell": "rm_scope {{ param }}",
},
},
},
},
}
all = {"base": plugin_base}
providers = {"base": plugin_base}

View File

@ -2,9 +2,9 @@ import os
from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml
all = None
providers = None
yml_dir = get_pkg_dir(__name__)
conf_files = open_yaml(os.path.join(yml_dir, "devops.yml"))
for conf in conf_files:
all = conf.get("providers", {})
providers = conf.get("providers", {})
break

View File

@ -1,10 +0,0 @@
import os
from pprint import pprint
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", {})

View File

@ -1,404 +0,0 @@
providers:
# Provider: SSH
# ==================
ssh:
services:
local.ssh_key:
desc: Local ssh key
inputs:
ssh_key_secret: ""
ssh_key_alg: "ed25519"
commands:
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}
echo mkdir -p $HOME/.ssh/{{ident}}/
echo ssh-keygen -f "${SSH_KEY_FILE}" \
-t ed25519 -a 100 \
{% if ssh_key_secret %}-N "{{ssh_key_secret}}"{%endif%} \
-C "$SSH_KEY_COMMENT"
ssh delete:
desc: Delete existing SSH key
cmd: |
find $HOME/.ssh/{{ident}}/ -name "{{user}}_*"
resources_def:
auth.ssh_certificate:
desc: SSH Certificates
input:
ssh_cert_file: null
needs:
- auth.ssh_key
auth.ssh_key:
desc: SSH Keypair
input:
ssh_key_file: null
ssh_key_secret: null
needs:
- kind: auth.password
remap:
ssh_key_secret: passord
account.ssh:
desc: Unix account
input:
host: null
# service.local.ssh_key:
# desc: A local ssh key
# resources:
# service.local.ssh_agent:
# enabled: true
# service.local.ssh_agent_keys:
# enabled: true
# 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}
# loop_limit: 3
# Provider: GPG Agent
# ==================
gpg_agent:
resources_def:
auth.gpg_key:
desc: GPG keypair
input:
gpg_key_file: null
gpg_key_secret: null
needs:
- kind: auth.password
remap:
gpg_key_secret: passord
# Provider: SSH Agent
# ==================
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_start:
desc: Start ssh-agent
shell: |
socket=$HOME/.local/state/ssh-agent/{{user}}
start=true
running=2
if [[ ! -e "$socket.env" ]]; then
running=2
elif [[ -e "$socket" ]]; then
running=$(SSH_AUTH_SOCK=$socket ssh-add -l &>/dev/null; echo $rc)
fi
if [[ "$running" -eq 2 ]]; then
# Start agent
>&2 echo "Start ssh-agent for {{ident}}"
mkdir -p "${socket%/*}"
ssh-agent -a $socket -t {{ssh_agent_tmout}} > $socket.env
fi
unset socket start running
# if [[ -d "/run/user/$(id -u)" ]]; then
# socket=/run/user/$(id -u)/ssh-agent/{{user}}
# else
# fi
shell_enable:
desc: Enable ssh-agent
shell: |
socket=$HOME/.local/state/ssh-agent/{{user}}
if [[ -e "$socket.env" ]]; then
# >&2 echo "Enable ssh-agent for {{ident}}"
source "$socket.env" >/dev/null
fi
unset socket
shell_disable:
desc: Disable ssh-agent
shell: |
unset SSH_AUTH_SOCK SSH_AGENT_PID
shell_stop:
desc: Kill ssh-agent
shell: |
socket=$HOME/.local/state/ssh-agent/{{user}}
if [[ -e "$socket.env" ]]; then
# >&2 echo "Enable ssh-agent for {{ident}}"
source "$socket.env" >/dev/null
# fi
# if [[ -n "$SSH_AGENT_PID" ]]; then
>&2 echo "Kill ssh-agent for {{ident}}"
eval "(ssh-agent -k)" >/dev/null
[[ -e "$socket.env" ]] && rm "$socket.env" || true
fi
unset socket
# env_file="$HOME/.local/state/ssh-agent/{{user}}.env"
# if [[ -f "$env_file" ]]; then
# source "$env_file"
# fi
# if [[ -f "$env_file" ]]; then
# rm "$env_file"
# fi
local.ssh_agent_keys:
desc: Local ssh-agent keys
commands:
ssh add:
desc: Unload keys into ssh-agent
shell: ssh-agent -d {ssh_key_file}
ssh rm:
desc: Load keys into ssh-agent
shell: |
ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %}
required_services:
- local.ssh_agent
resources_def:
service.local.ssh_agent:
desc: Configure ssh-agent daemon
service.local.ssh_agent_keys:
desc: Configure ssh-agent keys autoloader
resources:
service.local.ssh_agent:
enabled: true
service.local.ssh_agent_keys:
enabled: true
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}
loop_limit: 3
# Provider: Git Config
# ==================
git:
services:
local.git:
desc: Git identity
# input:
# ssh_agent_socket_dir: /run/user/ssh-agent
# ssh_agent_tmout: 7d
commands:
shell_enable:
desc: Enable git identity
shell: |
export GIT_AUTHOR_NAME='{{ident}}'
export GIT_AUTHOR_EMAIL='{{email}}'
export GIT_COMMITTER_NAME='{{ident}}'
export GIT_COMMITTER_EMAIL='{{email}}'
shell_disable:
desc: Disable git identity
shell: |
unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
local.git_home:
desc: Home as git repo
input:
git_dir: "$HOME"
git_work_tree: $HOME/.local/share/home_git
commands:
shell_enable:
desc: Enable git home management
shell: |
export GIT_DIR="{{git_dir}}"
export GIT_WORK_TREE="{{git_work_tree}}/{{ ident }}"
shell_disable:
desc: Disable git home management
shell: |
unset GIT_DIR GIT_WORK_TREE
required_services:
- local.git
resources_def:
service.local.git:
desc: Configure git
service.local.git_home:
desc: Configure home as git repo
resources:
service.local.git:
enabled: true
uses:
- account:{user}
# Disabled by default
service.local.git_home:
# Provider: PS1 Config
# ==================
ps1:
services:
local.ps1:
desc: PS1 prompt
input:
enabled: True
commands:
shell_enable:
desc: Enable PS1
shell: |
export OLD_PS1=$PS1
export PS1="\033[0;34m\]({{ident}})\033[00m\] ${PS1}"
shell_disable:
desc: Disable PS1
shell: |
export PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
# export PS1="$OLD_PS1"
resources_def:
service.local.ps1:
desc: PS1 prompt
resources:
service.local.ps1:
desc: Custom Ident PS1
# EXISTING
# WARN__: Your workspace is already activated
# NOTICE: Enabling id ...
# export SHELL_ID='mrjk'
# export GIT_AUTHOR_NAME='mrjk'
# export GIT_AUTHOR_EMAIL='mrjk.78@gmail.com'
# export GIT_COMMITTER_NAME='mrjk'
# export GIT_COMMITTER_EMAIL='mrjk.78@gmail.com'
# NOTICE: Enabling gpg ...
# export GNUPGHOME=/home/jez/.config/gpg/mrjk
# export GPG_AGENT_INFO=/run/user/1000/pgp-agent/mrjk/socket
# export GPG_DEFAULT_ID=mrjk
# export GPG_TTY=/dev/pts/48
# export GNUPGHOME=/home/jez/.config/gpg/mrjk
# NOTICE: Enabling ssh ...
# export SSH_AUTH_SOCK=/run/user/1000/ssh-agent/mrjk/socket
# NOTICE: Enabling gh ...
# export GH_TOKEN="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"
# export GH_REPO="mrjk"
# NOTICE: Enabling gitea ...
# export GITEA_SERVER_URL="ad808bc88fa37bce5e3bb963f1420aa575194d30"
# export GITEA_LOGIN="mrjk@git.jeznet.org"
# NOTICE: Enabling ps1 ...
# export PS1="\[\](mrjk)\[\] ${IDM_SHELL_PS1}"
# NOTICE: Identity 'mrjk' is loaded