# NOTE: # ──────────────────────────────────────< distros.sh >────────────────────────────────────── # ╭──────────────────────────────╮ # │ User variables and functions │ # ╰──────────────────────────────╯ distro="" ubuntu="false" debian="false" arch="false" fedora="false" alpine="false" opensuse="false" # Basic Colors BLACK=$'\e[30m' RED=$'\e[31m' GREEN=$'\e[32m' YELLOW=$'\e[33m' BLUE=$'\e[34m' MAGENTA=$'\e[35m' CYAN=$'\e[36m' WHITE=$'\e[37m' # Bright Colors BRIGHT_BLACK=$'\e[90m' BRIGHT_RED=$'\e[91m' BRIGHT_GREEN=$'\e[92m' BRIGHT_YELLOW=$'\e[93m' BRIGHT_BLUE=$'\e[94m' BRIGHT_MAGENTA=$'\e[95m' BRIGHT_CYAN=$'\e[96m' BRIGHT_WHITE=$'\e[97m' # Background Colors (standard) BG_BLACK=$'\e[40m' BG_RED=$'\e[41m' BG_GREEN=$'\e[42m' BG_YELLOW=$'\e[43m' BG_BLUE=$'\e[44m' BG_MAGENTA=$'\e[45m' BG_CYAN=$'\e[46m' BG_WHITE=$'\e[47m' # Background Bright Colors BG_BRIGHT_BLACK=$'\e[100m' BG_BRIGHT_RED=$'\e[101m' BG_BRIGHT_GREEN=$'\e[102m' BG_BRIGHT_YELLOW=$'\e[103m' BG_BRIGHT_BLUE=$'\e[104m' BG_BRIGHT_MAGENTA=$'\e[105m' BG_BRIGHT_CYAN=$'\e[106m' BG_BRIGHT_WHITE=$'\e[107m' # Styles BOLD=$'\e[1m' ITALIC=$'\e[3m' UNDERLINE=$'\e[4m' BLINK=$'\e[5m' # May not work in all terminals INVERT=$'\e[7m' # Invert foreground/background STRIKE=$'\e[9m' # Strikethrough # Reset NC=$'\e[0m' # Reset all styles/colors # INFO: # ↓ should get set in the install script itself # ↓ # echo with $PACKAGE and first argument, if 2 exist echo_pkg() { # if arg 2 does not exist, use normal echo if [[ -z $2 ]]; then case "$1" in build) echo "${BOLD}${BRIGHT_RED}${PACKAGE:-PKG}-build:${NC}${BRIGHT_BLUE} Building $PACKAGE ${NC}" ;; clone) echo "${BOLD}${BRIGHT_RED}${PACKAGE:-PKG}-clone:${NC}${BRIGHT_YELLOW} Cloning $PACKAGE sources..${NC}" ;; install) echo "${BOLD}${BRIGHT_RED}${PACKAGE:-PKG}-install:${NC}${BRIGHT_GREEN} Installing $PACKAGE now!${NC}" ;; *) echo "${BOLD}${BRIGHT_RED}${PACKAGE:-PKG}:${NC}${YELLOW} $1 ${NC}" ;; esac else case "$1" in deps | dep | dependencies) echo "${BOLD}${BRIGHT_RED}${PACKAGE:-PKG}-${pkger:-dependencies}:${BRIGHT_YELLOW} $2 ${NC}" ;; *) echo "${BOLD}${BRIGHT_RED}${PACKAGE:-PKG}-$1:${NC}${BRIGHT_YELLOW} $2 ${NC}" ;; esac fi } # alias for echo_pkg echo-pkg() { echo_pkg "$@" } echo-error() { echo "${BOLD}${RED}${UNDERLINE}ERROR:${NC}${BRIGHT_RED} $1 ${NC}" >&2 } command-exists() { command -v "$@" >/dev/null 2>&1 } silentexec() { "$@" >/dev/null 2>&1 } run-silent() { if [[ -z $silent ]] || ! $silent; then "$@" else silentexec "$@" fi } # if given an array, it checks if the command is available, and if not - installs all packages in that array one by one check-and-install() { local pkglength="${#@}" # if ! pen grey "Packages to install: $(pen green bold $pkglength)"; then # exit 1 # fi pen grey "Packages to install: $(pen green bold $pkglength)" # for a in "$@"; do # pen red bold "DEBUG: $a" # done line for pkg in "$@"; do if ! command-exists $pkg; then spin bold "$(pen bold yellow Installing) $pkg" if run --err err pkg-install $pkg; then upclear check "$(pen bold green Installed) $pkg" else upclear throw "Something went wrong! Could not install $(pen bold red $pkg)" echo-error "${err:-}" fi else upclear check bold "$pkg $(pen grey bold 'was already installed')" fi sleep 0.001 done } # ─< Check if the user is root and set sudo variable if necessary >─────────────────────── check_env() { if [ "$(id -u)" -ne 0 ]; then if command-exists sudo; then _sudo="sudo -E" pen blue "Testing your access.." line $_sudo echo "${GREEN}${BOLD}Aye, it works!${NC}" else echo-error "No sudo found and you're not root! Can't install packages." return 69 fi else pen bold red "Root access confirmed." _sudo="" fi checkFileAge() { local file="$1" [[ ! -e "$file" ]] && return 2 # File doesn't exist if [[ $(find "$file" -mtime +7 -print) ]]; then return 69 # File is older than 1 week else return 0 # File is within 1 week fi } } # CAUTION: # ╭──────────────────────────────────────────────────────────────────────╮ # │ This can break really quickly, since the pkg-remove function removes │ # │ without confirmation! use with CAUTION!! │ # ╰──────────────────────────────────────────────────────────────────────╯ _setup() { case "$1" in debian | ubuntu) pkg-install() { # $_sudo apt-get install --assume-yes "$@" if command-exists nala; then pkger=nala $_sudo nala install --assume-yes "$@" else pkger=apt-get $_sudo apt-get install --assume-yes "$@" fi } # CAUTION: pkg-remove() { if command-exists nala; then pkger=nala $_sudo nala remove --assume-yes "$@" $_sudo nala autoremove --assume-yes $_sudo nala autopurge --assume-yes else pkger=apt-get $_sudo apt-get remove --assume-yes "$@" $_sudo apt-get autoremove --assume-yes fi } ;; fedora) pkg-install() { pkger=dnf $_sudo dnf -y install "$@" } # CAUTION: pkg-remove() { pkger=dnf $_sudo dnf -y remove "$@" } ;; arch) pkg-install() { if command-exists paru; then pkger=paru paru -S --color always --noconfirm --needed "$@" elif command-exists yay; then pkger=yay yay -S --color always --noconfirm --needed "$@" else pkger=pacman $_sudo pacman -S --color always --noconfirm --needed "$@" fi } # CAUTION: pkg-remove() { if command-exists paru; then pkger=paru paru -R --color always --noconfirm "$@" elif command-exists yay; then pkger=yay yay -R --color always --noconfirm "$@" else pkger=pacman $_sudo pacman -R --color always --noconfirm "$@" fi } ;; opensuse) pkg-install() { pkger=zypper $_sudo zypper in "$@" } # CAUTION: pkg-remove() { pkger=zypper $_sudo zypper rem "$@" } ;; alpine) pkg-install() { pkger=apk apk add "$@" } # CAUTION: pkg-remove() { pkger=apk apk remove "$@" } ;; esac } # ─< Distribution detection and installation >──────────────────────────────────────── get_packager() { # ─< define fallback function >─────────────────────────────────────────────────────────── fallback() { local pkger="" for pkg in apt-get dnf pacman apk zypper; do if command-exists $pkg; then printf "Using ${RED}${pkg}${NC} method.." pkger="$pkg" break fi done case "$pkger" in apt-get) ubuntu="true" debian="true" distro="debian" ;; dnf) fedora="true" distro="fedora" ;; apk) alpine="true" distro="alpine" ;; pacman) arch="true" distro="arch" ;; zypper) opensuse="true" distro="opensuse" ;; *) pen red bold "Can not detect distribution correctly!" return 69 ;; esac } if [ -e /etc/os-release ]; then upclear pen bold grey "Detecting distribution..." . /etc/os-release # ─< Convert $ID and $ID_LIKE to lowercase >────────────────────────────────────────────── ID=$(printf "%s" "$ID" | tr '[:upper:]' '[:lower:]') ID_LIKE=$(printf "%s" "$ID_LIKE" | tr '[:upper:]' '[:lower:]') case "$ID" in ubuntu | pop | zorin | kubuntu | linuxmintubuntu) ubuntu="true" distro="ubuntu" ;; debian | kali | linuxmint | elementary | neon | kdeneon | deepin) debian="true" distro="debian" ;; fedora | centos | rhel | rocky | almalinux) fedora="true" distro="fedora" ;; alpine) alpine="true" distro="alpine" ;; arch | manjaro | garuda | endeavour) arch="true" distro="arch" ;; opensuse*) opensuse="true" distro="opensuse" ;; *) if [ "${ID_LIKE#*debian}" != "$ID_LIKE" ]; then debian="true" distro="debian" elif [ "${ID_LIKE#*ubuntu}" != "$ID_LIKE" ]; then ubuntu="true" distro="ubuntu" elif [ "${ID_LIKE#*arch}" != "$ID_LIKE" ]; then arch="true" distro="arch" elif [ "${ID_LIKE#*fedora}" != "$ID_LIKE" ]; then fedora="true" distro="fedora" elif [ "${ID_LIKE#*suse}" != "$ID_LIKE" ]; then opensuse="true" distro="opensuse" else pen bold red "Unsupported distribution: $ID" spin bold "Trying other methods.." sleep 1 run fallback && check "Got packager.." || throw "No chance helping you now buddy!" fi ;; esac else pen red bold "Unable to detect distribution - /etc/os-release not found." spin bold "Trying other methods.." sleep 1 if run fallback; then upclear check "Got packager.." else upclear throw "No chance helping you now buddy!" fi fi } # INFO: # ╭────────────────────────────────────────────────────────────────────────────────────────╮ # │ Automated setup for refreshing repositories and overall getting the variables to setup │ # ╰────────────────────────────────────────────────────────────────────────────────────────╯ dist_setup() { case "$distro" in debian) repen bold "Found $(pen red bold $distro)" _setup debian # Codename support if [ -n $VERSION_CODENAME ]; then export $VERSION_CODENAME=$VERSION_CODENAME # case "$VERSION_CODENAME" in # trixie) trixie=true ;; # bookworm) bookworm=true ;; # bullseye) bullseye=true ;; # buster) buster=true ;; # esac fi ;; ubuntu) repen bold "Found $(pen yellow bold $distro)" _setup ubuntu # Codename support if [ -n $VERSION_CODENAME ]; then export $VERSION_CODENAME=$VERSION_CODENAME # case "$VERSION_CODENAME" in # noble) noble=true ;; # jammy) jammy=true ;; # focal) focal=true ;; # bionic) bionic=true ;; # esac fi ;; fedora) repen bold "Found $(pen blue bold $distro)" _setup fedora # Add version-specific var like: fedora_40=true fedora_version="fedora_${VERSION_ID//./_}" eval "$fedora_version=true" ;; arch) repen bold "Found $(pen blue bold $distro)" _setup arch if command-exists yay || command_exists paru; then aur=true else aur=false fi ;; alpine) repen bold "Found $(pen blue bold $distro)" _setup $distro ;; opensuse) repen bold "Found $(pen green bold $distro)" _setup $distro ;; esac } update-package-list() { local USER="${USER:-$(whoami)}" repo::check() { check "${distro}-repositories up to date" || upclear throw "Error while updating $distro repositories.." } case "$distro" in ubuntu | debian) spin bold red "Refreshing $distro sources.." # sleep 1 if run $_sudo apt-get update; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi ;; fedora) spin blue bold "Refreshing $distro repositories.." if run $_sudo dnf update; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi ;; arch) if command-exists pacman; then if ! $aur; then pen grey bold "If this is your first time after installation (or simmilarly soon) then this might take a while.." spin bold grey "You don't have an AUR helper, gonna install one for ya!" local paruBuildDir="/opt/builds" silentexec $_sudo mkdir -p "$paruBuildDir" # echo "${YELLOW}Installing paru as AUR helper...${NC}" run $_sudo pacman -S --needed --noconfirm base-devel git || throw "Error installing dependencies.." local url="https://aur.archlinux.org/paru-bin.git" cd "$paruBuildDir" # && echo "${YELLOW} Cloning paru from ${NC}https://aur.archlinux.org/paru-bin.git" run $_sudo git clone $url paru || throw "Error cloning sources from $url" run $_sudo chown -R "$USER": "$paruBuildDir/paru" cd "$paruBuildDir/paru" local err if run --err err makepkg --noconfirm -si; then upclear check "Successfully installed $(pen red bold paru)" else upclear throw "Couldn't install paru!" echo-error "${err:-}" fi if command-exists paru; then check green bold "Paru is installed" else throw bold red "Something went wrong when installing paru!" fi fi fi spin blue bold "Refreshing $distro repositories.." # sleep 0.01 if command-exists paru; then if run paru -Sy; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi elif command-exists yay; then if run yay -Sy; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi else if run $_sudo pacman -Sy; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi fi ;; opensuse) spin bold green "Refreshing $distro repositories.." if run $_sudo zypper ref; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi ;; alpine) spin bold blue "Refreshing $distro repositories.." if run $_sudo apk update; then upclear check "Refreshed $distro sources.." else upclear throw "Error while updating $distro repositories.." fi ;; *) echo-error "Unsupported distribution: ${BRIGHT_RED}$distro" return 69 ;; esac } # NOTE: # ────────────────────────────────────< end distros.sh >──────────────────────────────────── # ───────────────────────────────────────< beddu.sh >─────────────────────────────────────── # # shellcheck disable=all # # beddu.sh - A lightweight bash framework for interactive scripts and pretty output # Version: v1.1.0 # # Copyright © 2025 Manuele Sarfatti # Licensed under the MIT license # See https://github.com/mjsarfatti/beddu readonly _q='?' readonly _a='❯' readonly _o='◌' readonly _O='●' readonly _mark='✓' readonly _warn='!' readonly _cross='✗' readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt readonly _spinner_frame_duration=0.15 up() { printf "\033[A" } down() { printf "\033[B" } bol() { printf "\r" } eol() { printf "\033[999C" } cl() { printf "\033[2K" } upclear() { up bol cl } line() { printf "\n" } show_cursor() { printf "\033[?25h" } hide_cursor() { printf "\033[?25l" } pen() { local new_line="\n" local text="${*: -1}" local args=("${@:1:$#-1}") local format_code="" local reset_code="\033[0m" for arg in "${args[@]}"; do arg=${arg,,} case "$arg" in -n) new_line="" ;; bold) format_code+="\033[1m" ;; italic) format_code+="\033[3m" ;; underline) format_code+="\033[4m" ;; black) format_code+="\033[30m" ;; red) format_code+="\033[31m" ;; green) format_code+="\033[32m" ;; yellow) format_code+="\033[33m" ;; blue) format_code+="\033[34m" ;; purple) format_code+="\033[35m" ;; cyan) format_code+="\033[36m" ;; white) format_code+="\033[37m" ;; grey | gray) format_code+="\033[90m" ;; [0-9]*) if [[ "$arg" =~ ^[0-9]+$ ]] && [ "$arg" -ge 0 ] && [ "$arg" -le 255 ]; then format_code+="\033[38;5;${arg}m" fi ;; *) ;; esac done printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}" } run() { local outvar_name errvar_name local -n outvar errvar # Declare namerefs (will be assigned below if needed) local cmd while [[ $# -gt 0 ]]; do case "$1" in --out) outvar_name="$2" shift 2 ;; --err) errvar_name="$2" shift 2 ;; *) cmd=("$@") break ;; esac done [[ -n "${outvar_name}" ]] && local -n outvar="${outvar_name}" [[ -n "${errvar_name}" ]] && local -n errvar="${errvar_name}" local stdout_file stderr_file stdout_file=$(mktemp) stderr_file=$(mktemp) "${cmd[@]}" >"${stdout_file}" 2>"${stderr_file}" local exit_code=$? [[ -n "${outvar_name}" ]] && outvar="$(<"$stdout_file")" [[ -n "${errvar_name}" ]] && errvar="$(<"$stderr_file")" rm -f "${stdout_file}" "${stderr_file}" return $exit_code } check() { if spinning; then spop upclear fi pen -n green "${_mark:-✓} " pen "$@" } repen() { upclear pen "$@" } trap "spop; show_cursor" EXIT INT TERM _spinner_pid="" _frame_duration="${_spinner_frame_duration:-0.1}" spin() { local message=("$@") local spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}" if spinning; then spop --keep-cursor-hidden fi ( hide_cursor trap "exit 0" USR1 line pen -n cyan "${spinner:0:1} " pen "${message[@]}" while true; do for ((i = 0; i < ${#spinner}; i++)); do frame="${spinner:$i:1}" up bol pen -n cyan "${frame} " pen "${message[@]}" sleep "$_frame_duration" done done ) & _spinner_pid=$! } spop() { local keep_cursor_hidden=false [[ "$1" == "--keep-cursor-hidden" ]] && keep_cursor_hidden=true if spinning; then kill -USR1 "${_spinner_pid}" 2>/dev/null sleep "$_frame_duration" if ps -p "${_spinner_pid}" >/dev/null 2>&1; then kill "${_spinner_pid}" 2>/dev/null fi if [[ "$keep_cursor_hidden" == false ]]; then show_cursor fi _spinner_pid="" fi } spinning() { [[ -n "${_spinner_pid}" ]] } throw() { if spinning; then spop upclear fi pen -n red "${_cross:-✗} " pen "$@" } warn() { if spinning; then spop upclear fi pen -n yellow bold italic "${_warn:-!} " pen italic "$@" } choose() { local -n outvar="$1" local prompt local options=("${@:3}") local current=0 local count=${#options[@]} prompt=$( pen -n blue "${_q:-?} " pen -n "${2} " pen gray "[↑↓]" ) hide_cursor trap 'show_cursor; return' INT TERM pen "$prompt" while true; do local index=0 for item in "${options[@]}"; do if ((index == current)); then pen -n blue "${_O:-●} " pen "${item}" else pen gray "${_o:-◌} ${item}" fi ((index++)) done read -s -r -n1 key if [[ $key == $'\e' ]]; then read -s -r -n2 -t 0.0001 escape key+="$escape" fi case "$key" in $'\e[A' | 'k') ((current--)) [[ $current -lt 0 ]] && current=$((count - 1)) ;; $'\e[B' | 'j') ((current++)) [[ $current -ge "$count" ]] && current=0 ;; 'q') upclear pen red bold "User exited the session with exit code 69!" pen red "This is $(pen bold red 'NOT') an accident!" exit 69 ;; '') break ;; esac echo -en "\e[${count}A\e[J" done outvar="${options[$current]}" } confirm() { local default="y" local hint="[Y/n]" local prompt local response while [[ $# -gt 0 ]]; do case "$1" in --default-no) default="n" hint="[y/N]" shift ;; --default-yes) shift ;; *) break ;; esac done prompt=$( pen -n blue "${_q:-?} " pen -n "$1" pen gray " $hint" pen -n blue "${_a:-❯} " ) show_cursor while true; do read -r -p "$prompt" response response="${response:-$default}" case "$response" in [Yy] | [Yy][Ee][Ss]) upclear pen -n blue "${_a:-❯} " pen "yes" return 0 ;; [Nn] | [Nn][Oo]) upclear pen -n blue "${_a:-❯} " pen "no" return 1 ;; [qQ] | quit) upclear pen red bold "User exited the session with exit code 69!" pen red "This is $(pen bold red 'NOT') an accident!" exit 69 ;; *) echo warn "Please answer yes or no." ;; esac done } request() { local -n outvar="$1" local prompt local answer prompt=$( pen -n blue "${_q:-?} " pen "${2}" pen -n blue "${_a:-❯} " ) show_cursor while true; do read -r -p "$prompt" answer case "$answer" in "") echo warn "Please type your answer." ;; *) break ;; esac done outvar="$answer" } seek() { local -n outvar="$1" local prompt local answer prompt=$( pen -n blue "${_q:-?} " pen "${2}" pen -n blue "${_a:-❯} " ) show_cursor read -r -p "$prompt" answer outvar="$answer" } # NOTE: # ──────────────────────────────────────< end beddu >──────────────────────────────────── if check_env; then # ─< gets the some variables like $aur, $debian, $ubuntu, $distro... >──────────────────── get_packager # ─< get more indepths variables like $trixie, $bookworm, $jammy, $fedora_40.. >────────── dist_setup # WHY: # ╭─────────────────────────────────────────────────────────────────────────╮ # │ check if the script has run at least once, so that the sources dont │ # │ have to get updated again.. │ # ╰─────────────────────────────────────────────────────────────────────────╯ pikaCheckFile="$HOME/.cache/pika_script_detection" if [ ! -f "$pikaCheckFile" ]; then pen bold grey "First time importing.." if update-package-list; then silentexec touch "$pikaCheckFile" fi else echo_pkg "Skipping repo refresh" fi fi