From de2e8071ab28665f16704c4cb02a398804d30955 Mon Sep 17 00:00:00 2001 From: Marco Gallegos Date: Tue, 18 Nov 2025 13:15:06 -0600 Subject: [PATCH] feat: Enhance Zsh configuration and setup scripts with improved plugin management and GNOME Keyring integration --- .zshrc | 12 +++- Readme.md | 14 ++-- modules/apps.sh | 66 ++++++++++++++++++ modules/common.sh | 10 +++ modules/disk-format.sh | 0 modules/zsh-config.sh | 37 ++++++++++ omarchy-setup.sh | 155 +++++++++++++++++++++++++++++++++-------- 7 files changed, 257 insertions(+), 37 deletions(-) mode change 100644 => 100755 modules/disk-format.sh diff --git a/.zshrc b/.zshrc index 3692c8e..d258436 100644 --- a/.zshrc +++ b/.zshrc @@ -38,9 +38,17 @@ zstyle ':completion:*' menu select # Cargar plugins específicos (zsh-autosuggestions y zsh-syntax-highlighting) [ -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" ] && \ source "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" +if [ ! -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" ] && \ + [ -r "/usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" ]; then + source "/usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" +fi [ -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ] && \ source "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" +if [ ! -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ] && \ + [ -r "/usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ]; then + source "/usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" +fi # --- Oh My Posh -------------------------------------------------------------- # Asegúrate de que Oh My Posh esté instalado y el tema 'catppuccin_frappe.omp.json' @@ -289,9 +297,9 @@ ytls() { # --- GNOME Keyring ----------------------------------------------------------- # Iniciar gnome-keyring-daemon si la sesión es gráfica y no está corriendo -if [ -n "$DESKTOP_SESSION" ]; then +if [ -n "$DESKTOP_SESSION" ] && command -v gnome-keyring-daemon >/dev/null 2>&1; then if ! pgrep -u "$USER" gnome-keyring-daemon > /dev/null 2>&1; then - eval $(gnome-keyring-daemon --start --components=pkcs11,secrets,ssh 2>/dev/null) + eval "$(gnome-keyring-daemon --start --components=pkcs11,secrets,ssh 2>/dev/null)" || true fi export SSH_AUTH_SOCK GPG_AGENT_INFO GNOME_KEYRING_CONTROL GNOME_KEYRING_PID fi diff --git a/Readme.md b/Readme.md index ef8eab9..87e0148 100644 --- a/Readme.md +++ b/Readme.md @@ -56,14 +56,14 @@ Selecciona las opciones que deseas instalar: 5) 🖨️ Configurar Impresoras (CUPS) 6) 🖱️ Instalar Tema de Cursor (Bibata) 7) 🎨 Gestionar Temas de Iconos (Papirus, Tela, etc.) - 8) 🎬 Instalar DaVinci Resolve (Intel Edition) - A) ✅ Instalar Todo (opciones 1, 2, 3, 4, 5, 6, 8) - F) 💾 Formatear un Disco (FAT32, exFAT, NTFS, ext4) + F) 💾 Habilitar Formatos FAT/exFAT/NTFS/ext4 H) 🎨 Instalar Configuración de Hyprland + R) 🎬 Instalar DaVinci Resolve (Intel Edition) + A) ✅ Instalar Todo (opciones 1, 2, 3, 4, 5, 6, 7, F, H) 0) 🚪 Salir ``` -> ℹ️ **Nota:** La opción `A) Instalar Todo` ejecuta los módulos 1, 2, 3, 4, 5, 6 y 8. Antes de usarla asegúrate de haber descargado manualmente el instalador de DaVinci Resolve (ZIP) en `~/Downloads/`. +> ℹ️ **Nota:** La opción `A) Instalar Todo` ejecuta los módulos 1, 2, 3, 4, 5, 6, 7, F y H. DaVinci Resolve (`R`) no se incluye aquí; instálalo manualmente cuando ya tengas el ZIP en `~/Downloads/`. ## 📋 Módulos Disponibles @@ -95,7 +95,11 @@ Selecciona las opciones que deseas instalar: ### 7. 🎨 Gestor de Iconos (`icon_manager.sh`) - Menú interactivo para instalar y cambiar entre temas de iconos como Papirus, Tela y Candy. -### 8. 🎬 DaVinci Resolve (`davinci-resolve.sh`) +### F. 💾 Soporte de Formatos (`disk-format.sh`) +- Instala utilidades para FAT32, exFAT, NTFS y ext4 +- Añade herramientas gráficas (GParted, GNOME Disks) para formateo manual + +### R. 🎬 DaVinci Resolve (`davinci-resolve.sh`) - Configuración de librerías y wrapper ## 🔧 Ejecutar Módulos Individualmente diff --git a/modules/apps.sh b/modules/apps.sh index de306b9..bb69d32 100755 --- a/modules/apps.sh +++ b/modules/apps.sh @@ -31,6 +31,7 @@ run_module_main() { local PACMAN_BASE=( git curl wget base-devel unzip htop fastfetch btop vim nano tmux xdg-utils xdg-user-dirs stow + gnome-keyring libsecret seahorse openssh rsync ) local PACMAN_MULTIMEDIA=( vlc vlc-plugins-all libdvdcss audacity inkscape @@ -145,6 +146,71 @@ run_module_main() { # Configurar servicios log_info "Configurando servicios..." + log_info "Configurando GNOME Keyring como agente de credenciales..." + mkdir -p "${HOME}/.config/environment.d" + cat <<'EOF' > "${HOME}/.config/environment.d/10-gnome-keyring.conf" +SSH_AUTH_SOCK=/run/user/$UID/keyring/ssh +EOF + if systemctl --user enable --now gnome-keyring-daemon.socket gnome-keyring-daemon.service >/dev/null 2>&1; then + log_success "GNOME Keyring listo para gestionar contraseñas y claves SSH." + else + log_warning "No se pudo habilitar gnome-keyring-daemon en systemd de usuario. Verifica que tu sesión use systemd (--user)." + fi + if command_exists gnome-keyring-daemon; then + local keyring_eval + keyring_eval="$(gnome-keyring-daemon --start --components=secrets,ssh 2>/dev/null)" || keyring_eval="" + if [[ -n "$keyring_eval" ]]; then + eval "$keyring_eval" + fi + fi + local keyring_socket="/run/user/$UID/keyring/ssh" + if [[ -S "$keyring_socket" ]]; then + export SSH_AUTH_SOCK="$keyring_socket" + fi + log_info "Vuelve a iniciar sesión para que las variables de entorno del keyring se apliquen." + + if command_exists ssh-add; then + local ssh_dir="${HOME}/.ssh" + if [[ -d "$ssh_dir" ]]; then + mapfile -t ssh_private_keys < <(find "$ssh_dir" -maxdepth 1 -type f -name "id_*" ! -name "*.pub" ! -name "*-cert.pub" 2>/dev/null) + if [[ ${#ssh_private_keys[@]} -gt 0 ]]; then + log_info "Agregando claves SSH detectadas al keyring (se solicitará la passphrase si aplica)..." + for key_path in "${ssh_private_keys[@]}"; do + if [[ ! -r "$key_path" ]]; then + log_warning "No se puede leer la clave $(basename "$key_path"); revísala manualmente." + continue + fi + if ssh-keygen -y -f "$key_path" >/dev/null 2>&1; then + log_info "Registrando clave $(basename "$key_path")..." + local spinner_was_active=0 + if [[ -n "${SPINNER_PID:-}" ]]; then + spinner_was_active=1 + fi + if declare -F pause_spinner >/dev/null; then + pause_spinner + fi + if SSH_AUTH_SOCK="$SSH_AUTH_SOCK" ssh-add "$key_path"; then + log_success "Clave $(basename "$key_path") añadida al keyring." + else + log_warning "No se pudo añadir la clave $(basename "$key_path")." + fi + if (( spinner_was_active )) && declare -F resume_spinner >/dev/null; then + resume_spinner + fi + else + log_warning "La clave $(basename "$key_path") parece inválida. Se omite." + fi + done + else + log_info "No se encontraron claves privadas SSH en ${ssh_dir}." + fi + else + log_info "No se detectó el directorio ~/.ssh; omitiendo carga de claves." + fi + else + log_warning "ssh-add no está disponible; no se pueden registrar claves en el keyring." + fi + # Habilitar keyd si está instalado if command_exists keyd; then log_info "Habilitando servicio keyd..." diff --git a/modules/common.sh b/modules/common.sh index b062ab7..dd1e828 100755 --- a/modules/common.sh +++ b/modules/common.sh @@ -14,19 +14,29 @@ NC='\033[0m' # No Color BOLD='\033[1m' # Funciones de logging +_maybe_clear_spinner() { + if declare -F spinner_clear_line >/dev/null; then + spinner_clear_line + fi +} + log_info() { + _maybe_clear_spinner echo -e "${BLUE}▶${NC} ${BOLD}$1${NC}" } log_success() { + _maybe_clear_spinner echo -e "${GREEN}✓${NC} ${GREEN}$1${NC}" } log_warning() { + _maybe_clear_spinner echo -e "${YELLOW}⚠${NC} ${YELLOW}$1${NC}" } log_error() { + _maybe_clear_spinner echo -e "${RED}✗${NC} ${RED}$1${NC}" } diff --git a/modules/disk-format.sh b/modules/disk-format.sh old mode 100644 new mode 100755 diff --git a/modules/zsh-config.sh b/modules/zsh-config.sh index 244bac4..0f787ee 100755 --- a/modules/zsh-config.sh +++ b/modules/zsh-config.sh @@ -29,6 +29,7 @@ install_zsh() { # --- 1. Instalar paquetes necesarios desde Pacman --- log_info "Instalando Zsh y herramientas esenciales..." local pkgs=( + git zsh zsh-completions zsh-syntax-highlighting @@ -79,6 +80,34 @@ install_zsh() { else log_info "Oh My Zsh ya está instalado." fi + + # Asegurar plugins personalizados de Oh My Zsh (zsh-autosuggestions, zsh-syntax-highlighting) + local zsh_custom="${target_ohmyzsh_dir}/custom" + local zsh_custom_plugins="${zsh_custom}/plugins" + mkdir -p "$zsh_custom_plugins" + + ensure_omz_plugin() { + local name="$1" + local repo="$2" + local plugin_path="${zsh_custom_plugins}/${name}" + + if [[ -d "${plugin_path}/.git" ]]; then + log_info "Actualizando plugin ${name}..." + git -C "$plugin_path" pull --ff-only >/dev/null 2>&1 || true + elif [[ -d "$plugin_path" ]]; then + log_info "Plugin ${name} ya existe." + else + log_info "Clonando plugin ${name}..." + if git clone --depth 1 "$repo" "$plugin_path" >/dev/null 2>&1; then + log_success "Plugin ${name} instalado." + else + log_warning "No se pudo clonar ${name}. Se usará la versión de los paquetes del sistema." + fi + fi + } + + ensure_omz_plugin "zsh-autosuggestions" "https://github.com/zsh-users/zsh-autosuggestions.git" + ensure_omz_plugin "zsh-syntax-highlighting" "https://github.com/zsh-users/zsh-syntax-highlighting.git" # --- 3. Descargar y configurar el .zshrc personalizado --- log_info "Actualizando configuración .zshrc..." @@ -145,6 +174,14 @@ install_zsh() { fi fi + if command_exists oh-my-posh; then + local omp_completion_dir="${target_home}/.local/share/zsh/site-functions" + mkdir -p "$omp_completion_dir" + if oh-my-posh completion zsh > "${omp_completion_dir}/_oh-my-posh" 2>/dev/null; then + log_success "Autocompletado de Oh My Posh actualizado." + fi + fi + # --- 5. Cambiar el shell por defecto a Zsh para el usuario actual --- local current_shell current_shell="$(getent passwd "$target_user" 2>/dev/null | cut -d: -f7)" diff --git a/omarchy-setup.sh b/omarchy-setup.sh index b0227be..cdb7412 100755 --- a/omarchy-setup.sh +++ b/omarchy-setup.sh @@ -9,6 +9,7 @@ set -u SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MODULES_DIR="${SCRIPT_DIR}/modules" REPO_BASE="https://raw.githubusercontent.com/marcogll/omarchy_setup/main" +SUDO_PASSWORD="" # Verificar que los módulos existen if [[ ! -d "${MODULES_DIR}" ]] || [[ ! -f "${MODULES_DIR}/common.sh" ]]; then @@ -35,23 +36,59 @@ chmod +x "${MODULES_DIR}"/*.sh 2>/dev/null || true SPINNER_PID= SPINNER_DEVICE_ACTIVE= +SPINNER_MESSAGE= + +spinner_clear_line() { + local device="${SPINNER_DEVICE_ACTIVE:-/dev/tty}" + if [[ ! -w "$device" ]]; then + device="/dev/null" + fi + printf '\r\033[K' >"$device" 2>/dev/null || true +} + +pause_spinner() { + local device="${SPINNER_DEVICE_ACTIVE:-/dev/tty}" + if [[ ! -w "$device" ]]; then + device="/dev/null" + fi + if [[ -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then + kill "$SPINNER_PID" &>/dev/null || true + wait "$SPINNER_PID" &>/dev/null || true + fi + spinner_clear_line + printf '\033[?25h' >"$device" 2>/dev/null || true + SPINNER_PID= +} + +resume_spinner() { + if [[ -n "$SPINNER_MESSAGE" ]]; then + start_spinner "$SPINNER_MESSAGE" + fi +} # Inicia una animación de spinner en segundo plano # Uso: start_spinner "Mensaje..." start_spinner() { local message="$1" + if [[ -n "$SPINNER_PID" ]]; then + pause_spinner + fi + local device="/dev/tty" if [[ ! -w "$device" ]]; then device="/dev/null" fi + SPINNER_DEVICE_ACTIVE="$device" + SPINNER_MESSAGE="$message" ( local chars="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" local dev="$device" + local msg="$message" while :; do for (( i=0; i<${#chars}; i++ )); do - printf '\r\033[K%s %s' "${CYAN}${chars:$i:1}${NC}" "$message" >"$dev" + printf '\r\033[K%s %s' "${CYAN}${chars:$i:1}${NC}" "$msg" >"$dev" sleep 0.1 done done @@ -67,28 +104,60 @@ stop_spinner() { local exit_code=$1 local success_msg=$2 local error_msg=${3:-"Ocurrió un error"} - local device="${SPINNER_DEVICE_ACTIVE:-/dev/tty}" - if [[ ! -w "$device" ]]; then - device="/dev/null" - fi - if [[ -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then - kill "$SPINNER_PID" &>/dev/null - wait "$SPINNER_PID" &>/dev/null - fi - - # Limpiar la línea del spinner - printf '\r\033[K' >"$device" - + pause_spinner + if [[ $exit_code -eq 0 ]]; then log_success "$success_msg" else log_error "$error_msg" fi - # Restaurar cursor - printf '\033[?25h' >"$device" 2>/dev/null || true - SPINNER_PID= + SPINNER_DEVICE_ACTIVE= + SPINNER_MESSAGE= +} + +ensure_sudo_session() { + if sudo -n true 2>/dev/null; then + return 0 + fi + + if [[ -n "${SUDO_PASSWORD:-}" ]]; then + if printf '%s\n' "$SUDO_PASSWORD" | sudo -S -v >/dev/null 2>&1; then + return 0 + fi + SUDO_PASSWORD="" + log_warning "La contraseña de sudo almacenada no es válida. Se solicitará nuevamente." + fi + + pause_spinner + + local attempts=0 + while (( attempts < 3 )); do + if (( attempts == 0 )); then + log_info "Se requiere autenticación de sudo para continuar." + else + log_info "Intenta ingresar la contraseña nuevamente." + fi + local password_input="" + read -s -p "Contraseña de sudo: " password_input + echo "" + + if [[ -z "$password_input" ]]; then + log_warning "La contraseña no puede estar vacía." + elif printf '%s\n' "$password_input" | sudo -S -v >/dev/null 2>&1; then + SUDO_PASSWORD="$password_input" + log_success "Sesión de sudo autenticada." + return 0 + else + log_error "Contraseña de sudo incorrecta." + fi + + ((attempts++)) + done + + log_error "No se pudo autenticar con sudo después de varios intentos." + return 1 } # --- Definición de Módulos --- @@ -101,17 +170,17 @@ MODULES=( ["1"]="apps;run_module_main;📦 Instalar Aplicaciones (VS Code, VLC, drivers, etc.);bg" ["2"]="zsh-config;install_zsh;🐚 Configurar Zsh (shell, plugins, config);bg" ["3"]="docker;install_docker;🐳 Instalar Docker y Portainer;fg" - ["4"]="zerotier;install_zerotier;🌐 Instalar ZeroTier VPN;bg" + ["4"]="zerotier;install_zerotier;🌐 Instalar ZeroTier VPN;fg" ["5"]="printer;install_printer;🖨️ Configurar Impresoras (CUPS);bg" ["6"]="mouse_cursor;install_mouse_cursor;🖱️ Instalar Tema de Cursor (Bibata);bg" ["7"]="icon_manager;run_module_main;🎨 Gestionar Temas de Iconos (Papirus, Tela, etc.);fg" - ["8"]="davinci-resolve;install_davinci_resolve;🎬 Instalar DaVinci Resolve (Intel Edition);fg" - ["H"]="hyprland-config;run_module_main;🎨 Instalar Configuración de Hyprland;bg" ["F"]="disk-format;run_module_main;💾 Habilitar Formatos FAT/exFAT/NTFS/ext4;bg" + ["R"]="davinci-resolve;install_davinci_resolve;🎬 Instalar DaVinci Resolve (Intel Edition);fg" + ["H"]="hyprland-config;run_module_main;🎨 Instalar Configuración de Hyprland;bg" ) # Módulos a incluir en la opción "Instalar Todo" -INSTALL_ALL_CHOICES=("1" "2" "3" "4" "5" "6" "7" "8" "H") +INSTALL_ALL_CHOICES=("1" "2" "3" "4" "5" "6" "7" "F" "H") # Función para mostrar el menú show_menu() { @@ -129,7 +198,7 @@ show_menu() { echo -e " ${GREEN}${key})${NC} ${description}" done | sort -V - echo -e " ${GREEN}A)${NC} ✅ Instalar Todo (1, 2, 3, 4, 5, 6, 7, 8, H)" + echo -e " ${GREEN}A)${NC} ✅ Instalar Todo (1, 2, 3, 4, 5, 6, 7, F, H)" echo -e " ${GREEN}0)${NC} 🚪 Salir" echo "" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" @@ -203,7 +272,15 @@ install_all() { local failed=() for choice in "${INSTALL_ALL_CHOICES[@]}"; do + if [[ "$choice" == "R" ]]; then + continue + fi IFS=';' read -r module_file _ description type <<< "${MODULES[$choice]}" + + if ! ensure_sudo_session; then + failed+=("${module_file}") + continue + fi # Separador visual para cada módulo echo -e "\n${MAGENTA}────────────────────────────────────────────────────────────${NC}" @@ -237,9 +314,9 @@ install_all() { # Función principal main() { # Limpieza al salir: detener el spinner y restaurar el cursor - trap 'stop_spinner 1 "Script interrumpido." >/dev/null 2>&1; exit 1' INT TERM + trap 'stop_spinner 1 "Script interrumpido." >/dev/null 2>&1; unset SUDO_PASSWORD; exit 1' INT TERM # Limpieza final al salir normalmente - trap 'tput cnorm' EXIT + trap 'tput cnorm; unset SUDO_PASSWORD' EXIT # Verificar que estamos en Arch Linux if [[ ! -f /etc/arch-release ]]; then @@ -248,15 +325,15 @@ main() { fi # Verificar permisos de sudo - if ! sudo -n true 2>/dev/null; then - log_info "Este script requiere permisos de sudo" - sudo -v + if ! ensure_sudo_session; then + log_error "No se pudieron obtener privilegios de sudo. Saliendo." + exit 1 fi # Mantener sudo activo en background local parent_pid=$$ (while true; do - sudo -n true + sudo -n true >/dev/null 2>&1 sleep 60 kill -0 "$parent_pid" || exit done 2>/dev/null) & @@ -265,6 +342,10 @@ main() { # Exportar funciones para que los submódulos las puedan usar export -f start_spinner export -f stop_spinner + export -f pause_spinner + export -f resume_spinner + export -f spinner_clear_line + export -f ensure_sudo_session while true; do show_menu @@ -275,7 +356,7 @@ main() { IFS=';' read -r _ _ description type <<< "${MODULES[$choice]}" # Manejo especial para DaVinci Resolve - if [[ "$choice" == "8" ]]; then + if [[ "$choice" == "R" ]]; then log_warning "DaVinci Resolve requiere el ZIP de instalación en ~/Downloads/" echo -ne "${BOLD}¿Continuar con la instalación? [s/N]: ${NC} " read -r confirm @@ -286,6 +367,11 @@ main() { fi fi + if ! ensure_sudo_session; then + read -p "Presiona Enter para continuar..." + continue + fi + if [[ "$type" == "bg" ]]; then spinner_msg="${description#* }..." # "Instalar Apps..." start_spinner "${spinner_msg}" @@ -300,11 +386,14 @@ main() { fi echo "" + if declare -F pause_spinner >/dev/null; then + pause_spinner + fi read -p "Presiona Enter para continuar..." elif [[ "$choice" == "A" ]]; then - log_warning "La opción 'Instalar Todo' ejecutará los módulos: 1, 2, 3, 4, 5, 6, 7, 8 y H." - log_warning "DaVinci Resolve requiere que el ZIP de instalación esté en ~/Downloads/." + log_warning "La opción 'Instalar Todo' ejecutará los módulos: 1, 2, 3, 4, 5, 6, 7, F y H." + log_info "DaVinci Resolve (opción R) no se ejecutará en este lote; instálalo aparte cuando ya tengas el ZIP." echo -ne "${BOLD}¿Confirmas que deseas instalar todas las opciones ahora? [s/N]: ${NC}" read -r confirm if [[ "${confirm}" =~ ^[SsYy]$ ]]; then @@ -313,12 +402,18 @@ main() { log_info "Instalación cancelada" fi echo "" + if declare -F pause_spinner >/dev/null; then + pause_spinner + fi read -p "Presiona Enter para continuar..." elif [[ "$choice" == "0" ]]; then log_info "Saliendo..." exit 0 else log_error "Opción inválida. Presiona Enter para continuar..." + if declare -F pause_spinner >/dev/null; then + pause_spinner + fi read -r fi done