From 074131bb54eec6e92a76e49572b9fb66288ed090 Mon Sep 17 00:00:00 2001 From: Robert Perce Date: Thu, 29 Jan 2026 23:39:06 -0600 Subject: [PATCH] blind mgmt setup --- private_bin/private_argparse.bash | 76 ++++++++++++++++ private_bin/private_mgmt | 90 +++++++++++++++++++ private_dot_bashrc | 2 + .../private_modules/private_packages.bash | 32 +++++++ .../private_mgmt/private_packages | 1 + 5 files changed, 201 insertions(+) create mode 100644 private_bin/private_argparse.bash create mode 100755 private_bin/private_mgmt create mode 100644 private_dot_config/private_mgmt/private_modules/private_packages.bash create mode 100644 private_dot_config/private_mgmt/private_packages diff --git a/private_bin/private_argparse.bash b/private_bin/private_argparse.bash new file mode 100644 index 0000000..f0b8f10 --- /dev/null +++ b/private_bin/private_argparse.bash @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +declare -gA ARGP +declare -gA SHORT + +__reset_argparse() { + declare -gA ARGP + declare -gA SHORT +} + +die() { echo "Error: $*"; exit 1; } + +def_arg() { + test "$3" = bool -o "$3" = value || die "def_arg: type (\$3) must be 'bool' or 'value', received '$3'" + ARGP["$1,type"]="$3" + + if [ "$3" = bool ]; then + test -z "$4" || die "def_arg: bool args cannot specify required/default" + declare -g "$1=0" + else + test "$4" = required -o "$4" = default || die "def_arg: \$4 must be 'required' or 'default', received '$4'" + if [ "$4" = required ]; then + ARGP["$1,required"]=1 + else + value="$5" + declare -g "$1=$value" + fi + fi + + if [ -n "$2" ]; then + SHORT["$2"]="$1" + fi +} + +parse_args() { + positional=() + while (( $# > 0 )); do + next="$1"; shift + if [[ "$next" == --* ]]; then + arg=${next#--} + test -n "${ARGP["$arg,type"]}" || die "unknown arg --$arg" + if [ "${ARGP["$arg,type"]}" = bool ]; then + value=1 + elif [ $# -eq 0 ]; then + die "Missing value for --$arg" + else + value="$1"; shift + fi + declare -g "$arg=$value" + elif [[ "$next" == -* ]]; then + for ((i = 1 ; i < ${#next} ; i++ )); do + short=${next:$i:1} + arg=${SHORT["$short"]} + test -n "$arg" || die "unknown short arg -$short" + if [ "${ARGP["$arg,type"]}" = bool ]; then + declare -g "$arg=1" + else + value=${next:$(( i+1 ))} + if [ -z "$value" ]; then + value="$1"; shift + fi + test -n "$value" || die "missing value for -$short" + declare -g "$arg=$value" + break + fi + done + else + positional+=("$next") + fi + done + + # check for missing required + for arg in "${!ARGP[@]}"; do + arg="${arg%%,*}" + [ "${ARGP["$arg,required"]}" != 1 ] || [ -n "${!arg}" ] || die "missing argument --$arg" + done +} diff --git a/private_bin/private_mgmt b/private_bin/private_mgmt new file mode 100755 index 0000000..b4be9d5 --- /dev/null +++ b/private_bin/private_mgmt @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -e + +source argparse.bash +functions=; def_arg functions f value default "${XDG_CONFIG_HOME:-"$HOME/.config"}/mgmt/functions" +modules=; def_arg modules m value default "${XDG_CONFIG_HOME:-"$HOME/.config"}/mgmt/modules" +positional=(); parse_args "$@" + +while IFS= read -rd '' fnfile; do + source "$fnfile" +done < <(find "$functions" -type f -print0) + +PREFIX="" +grey() { printf "\33[37;2m%s\33[0;m" "$*"; } +red() { printf "\33[31;1m%s\33[0;m" "$*"; } +diag() { + >&2 printf "\33[2K\r%s" "$(grey "$PREFIX")" + >&2 echo "$@" +} + +join_by() { local IFS="$1"; shift; echo "$*"; } +filter_by_args() { grep -E "$(join_by '|' "${positional[@]}")"; } + +##### +# Gather dependencies of (filtered) modules +##### +declare -a found_modules +mapfile -t filtered_mods < <(find "$modules" -type f | filter_by_args) +set -- "${filtered_mods[@]}" +while [ $# -gt 0 ]; do + unset deps; declare -a deps + for mod; do + if echo "${found_modules[@]}" | grep -q "$mod"; then + continue + fi + found_modules+=( "$mod" ) + unset DEPENDS + source "$mod" + for dep in "${DEPENDS[@]}"; do + if echo "${found_modules[@]}" | grep -qv "$dep"; then + if echo "${deps[@]}" | grep -qv "$dep"; then + deps+=( "$dep" ) + fi + fi + done + done + set -- "${deps[@]}" +done + +##### +# Resolve dependency ordering +##### +declare -a ordered_modules +set -- "${found_modules[@]}" +while [ $# -gt 0 ]; do + unset remaining; declare -a remaining + for mod; do + unset DEPENDS + source "$mod" + if [[ -n "$(comm -23 <(printf -- '%s\n' "${DEPENDS[@]}" | sort) <(printf -- '%s\n' "${ordered_modules[@]}" | sort))" ]]; then + remaining+=( "$mod" ) + else + ordered_modules+=( "$mod" ) + fi + done + if [ $# -eq ${#remaining[@]} ]; then + diag "$(red "Error: dependency loop involving ${remaining[*]}")" + exit 1 + fi + set -- "${remaining[@]}" +done + +##### +# Apply modules +##### +sudo -v +set -- "${ordered_modules[@]}" +for mod; do + diag -e "\nApplying $mod" + PREFIX="[$mod] " + unset check_setup setup check_apply apply + source "$modules/$mod" + test "$(declare -F check_setup)" || { check_setup() { false ; } } + test "$(declare -F check_apply)" || { check_apply() { false ; } } + test "$(declare -F setup)" || { setup() { true; } } + test "$(declare -F apply)" || { apply() { true; } } + check_setup || setup + check_apply || apply + PREFIX="" +done diff --git a/private_dot_bashrc b/private_dot_bashrc index ca86fc2..c28caca 100755 --- a/private_dot_bashrc +++ b/private_dot_bashrc @@ -4,6 +4,8 @@ blue="\[\e[38;5;50m\]" rst="\[\e[0m\]" PS1="╭╢$blue\w$rst\n╰╼ " +PATH=$PATH:"$HOME/bin" + function conf() { fname="${XDG_CONFIG_HOME:-.config}/$1" test -f "$fname" && source "$fname" diff --git a/private_dot_config/private_mgmt/private_modules/private_packages.bash b/private_dot_config/private_mgmt/private_modules/private_packages.bash new file mode 100644 index 0000000..d8e87f3 --- /dev/null +++ b/private_dot_config/private_mgmt/private_modules/private_packages.bash @@ -0,0 +1,32 @@ +_os() { + if command -v termux-setup-storage >/dev/null; then + echo "termux" + else + grep '^ID=' /etc/os-release | cut -d= -f2 + fi +} + +missing=() +check_apply() { + while IFS= read -r package; do + cmd=$(echo "$package" | cut -d\; -f1) + pkg=$(echo "$package" | grep -Eo "$(_os):[^ ]+" || echo ":$cmd") + pkg=$(echo "$pkg" | cut -d: -f2) + [ -n "$pkg" ] || next + + command -v "$cmd" >/dev/null || missing+=( "$pkg" ) + done < ~/.config/mgmt/packages + + [ ${#missing} -eq 0 ] +} + +apply() { + case "$(_os)" in + ubuntu) manager="apt install" ;; + arch) manager="pacman -Syu" ;; + termux) manager="pkg install" ;; + *) return 1 ;; + esac + + $manager "${missing[@]}" +} diff --git a/private_dot_config/private_mgmt/private_packages b/private_dot_config/private_mgmt/private_packages new file mode 100644 index 0000000..13ce00b --- /dev/null +++ b/private_dot_config/private_mgmt/private_packages @@ -0,0 +1 @@ +nvim; termux:neovim arch:neovim ubuntu: