From 0ab9f62acedfcbed14d4ecf788a06de89e231c94 Mon Sep 17 00:00:00 2001 From: "Mariano Z." Date: Sun, 8 Jun 2025 23:28:56 -0300 Subject: [PATCH] dev: automated commit - 2025-06-08 23:28:55 --- common.sh | 252 ++++++++++++++++++++++++++++ main.go | 102 ++++++++---- runs/aur | 84 ++++++---- runs/base | 378 +++++++++++++++++++++++------------------- runs/boot | 70 ++++---- runs/docker | 47 ++++-- runs/dotfiles | 229 +++++++++++-------------- runs/limine | 211 ----------------------- runs/limine_hibernate | 279 +++++++++++++++++++++++++++++++ runs/nvim | 55 ++++++ runs/paru | 69 ++++++-- runs/services | 152 +++++++++-------- runs/sudo | 42 +++++ 13 files changed, 1255 insertions(+), 715 deletions(-) create mode 100644 common.sh delete mode 100755 runs/limine create mode 100755 runs/limine_hibernate create mode 100755 runs/nvim create mode 100755 runs/sudo diff --git a/common.sh b/common.sh new file mode 100644 index 0000000..dd9b00b --- /dev/null +++ b/common.sh @@ -0,0 +1,252 @@ +#!/usr/bin/env bash +# Script Standards and Common Functions +# Source this file in your scripts for consistent behavior + +# ============================================================================== +# STANDARD SCRIPT HEADER +# ============================================================================== +# All scripts should start with: +# #!/usr/bin/env bash +# # NAME: Brief description of what the script does +# # REQUIRES: sudo interactive (if needed) +# +# set -euo pipefail +# +# # Source common functions (adjust path as needed) +# source "$(dirname "$0")/common.sh" 2>/dev/null || true + +# ============================================================================== +# CONFIGURATION +# ============================================================================== +readonly SCRIPT_NAME="${0##*/}" +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Colors for output (only if terminal supports it) +if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then + readonly RED=$(tput setaf 1) + readonly GREEN=$(tput setaf 2) + readonly YELLOW=$(tput setaf 3) + readonly BLUE=$(tput setaf 4) + readonly PURPLE=$(tput setaf 5) + readonly CYAN=$(tput setaf 6) + readonly WHITE=$(tput setaf 7) + readonly BOLD=$(tput bold) + readonly RESET=$(tput sgr0) +else + readonly RED="" GREEN="" YELLOW="" BLUE="" PURPLE="" CYAN="" WHITE="" BOLD="" RESET="" +fi + +# ============================================================================== +# LOGGING FUNCTIONS +# ============================================================================== +log_info() { + printf "${GREEN}[INFO]${RESET} %s\n" "$*" +} + +log_warn() { + printf "${YELLOW}[WARN]${RESET} %s\n" "$*" >&2 +} + +log_error() { + printf "${RED}[ERROR]${RESET} %s\n" "$*" >&2 +} + +log_success() { + printf "${GREEN}[SUCCESS]${RESET} %s\n" "$*" +} + +log_debug() { + if [[ "${DEBUG:-0}" == "1" ]]; then + printf "${CYAN}[DEBUG]${RESET} %s\n" "$*" >&2 + fi +} + +log_sudo() { + printf "${PURPLE}[SUDO]${RESET} %s\n" "$*" +} + +# ============================================================================== +# UTILITY FUNCTIONS +# ============================================================================== +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +is_root() { + [[ $EUID -eq 0 ]] +} + +require_root() { + if ! is_root; then + log_error "This script requires root privileges" + log_info "Run with: sudo $0 $*" + exit 1 + fi +} + +# Check if running in interactive terminal +is_interactive() { + [[ -t 0 && -t 1 ]] +} + +# ============================================================================== +# CONFIRMATION FUNCTIONS +# ============================================================================== +confirm() { + local prompt="${1:-Continue?}" + local default="${2:-n}" + local response + + if ! is_interactive; then + log_warn "Non-interactive mode, using default: $default" + [[ "$default" == "y" ]] + return $? + fi + + while true; do + if [[ "$default" == "y" ]]; then + printf "%s [Y/n]: " "$prompt" + else + printf "%s [y/N]: " "$prompt" + fi + + read -r response + response=${response,,} # Convert to lowercase + + case "$response" in + "") + [[ "$default" == "y" ]] + return $? + ;; + y | yes) + return 0 + ;; + n | no) + return 1 + ;; + *) + log_warn "Please answer 'y' or 'n'" + ;; + esac + done +} + +# Show changes and confirm +confirm_change() { + local file="$1" + local description="$2" + local temp_file="$3" + + log_info "Proposed changes for $description:" + log_info "File: $file" + echo + + if command_exists colordiff; then + colordiff -u "$file" "$temp_file" 2>/dev/null || diff -u "$file" "$temp_file" 2>/dev/null || true + else + diff -u "$file" "$temp_file" 2>/dev/null || true + fi + + echo + confirm "Apply these changes?" +} + +# ============================================================================== +# FILE OPERATIONS +# ============================================================================== +backup_file() { + local file="$1" + local backup_suffix="${2:-$(date +%Y%m%d_%H%M%S)}" + local backup_file="${file}.backup.${backup_suffix}" + + if [[ -f "$file" ]]; then + cp "$file" "$backup_file" + log_info "Created backup: $backup_file" + echo "$backup_file" + fi +} + +create_temp_file() { + local template="${1:-tmp.XXXXXX}" + mktemp "/tmp/${template}" +} + +# ============================================================================== +# PACKAGE MANAGEMENT +# ============================================================================== +install_packages() { + local packages=("$@") + + if [[ ${#packages[@]} -eq 0 ]]; then + log_warn "No packages specified" + return 1 + fi + + log_info "Installing packages: ${packages[*]}" + + if command_exists paru; then + paru -S --needed --noconfirm "${packages[@]}" + elif command_exists pacman; then + sudo pacman -S --needed --noconfirm "${packages[@]}" + else + log_error "No supported package manager found" + return 1 + fi +} + +# ============================================================================== +# SERVICE MANAGEMENT +# ============================================================================== +enable_service() { + local service="$1" + local user_service="${2:-false}" + + if [[ "$user_service" == "true" ]]; then + log_info "Enabling user service: $service" + systemctl --user enable --now "$service" + else + log_info "Enabling system service: $service" + sudo systemctl enable --now "$service" + fi +} + +# ============================================================================== +# ERROR HANDLING +# ============================================================================== +cleanup() { + local exit_code=$? + # Add any cleanup operations here + exit $exit_code +} + +# Set up trap for cleanup +trap cleanup EXIT INT TERM + +# ============================================================================== +# SCRIPT INITIALIZATION +# ============================================================================== +init_script() { + log_info "Starting $SCRIPT_NAME" + + # Set DEBUG mode if requested + if [[ "${1:-}" == "--debug" ]]; then + export DEBUG=1 + log_debug "Debug mode enabled" + shift + fi +} + +# ============================================================================== +# SCRIPT COMPLETION +# ============================================================================== +finish_script() { + local exit_code="${1:-0}" + + if [[ "$exit_code" -eq 0 ]]; then + log_success "$SCRIPT_NAME completed successfully" + else + log_error "$SCRIPT_NAME failed with exit code $exit_code" + fi + + exit "$exit_code" +} diff --git a/main.go b/main.go index 418cb2a..d85e270 100644 --- a/main.go +++ b/main.go @@ -32,11 +32,12 @@ type Config struct { } type Script struct { - Path string // Full filesystem path - Name string // Just filename (e.g. "install.sh") - RelPath string // Relative path from runs/ (e.g. "tools/install.sh") - Desc string // Description from script comment - RequiresSudo bool // Whether script needs elevated privileges + Path string // Full filesystem path + Name string // Just filename (e.g. "install.sh") + RelPath string // Relative path from runs/ (e.g. "tools/install.sh") + Desc string // Description from script comment + RequiresSudo bool // Whether script needs elevated privileges + RequiresInteractive bool // Whether script needs interactive input } var config Config @@ -213,15 +214,20 @@ func handlePush() error { return nil } -func getScriptMetadata(scriptPath string) (string, bool) { +type ScriptMetadata struct { + RequiresSudo bool + RequiresInteractive bool +} + +func getScriptMetadata(scriptPath string) (string, ScriptMetadata) { file, err := os.Open(scriptPath) if err != nil { - return "No description", false + return "No description", ScriptMetadata{} } defer file.Close() var desc string - var requiresSudo bool + scriptMetadata := ScriptMetadata{} scanner := bufio.NewScanner(file) for scanner.Scan() { @@ -231,13 +237,9 @@ func getScriptMetadata(scriptPath string) (string, bool) { desc = strings.TrimSpace(strings.TrimPrefix(line, "# NAME:")) } - if strings.HasPrefix(line, "# REQUIRES: sudo") || - strings.HasPrefix(line, "# REQUIRES:sudo") || - strings.HasPrefix(line, "# ELEVATED: true") || - strings.HasPrefix(line, "# ELEVATED:true") || - strings.HasPrefix(line, "# SUDO: true") || - strings.HasPrefix(line, "# SUDO:true") { - requiresSudo = true + if strings.HasPrefix(line, "# REQUIRES:") { + scriptMetadata.RequiresSudo = strings.Contains(line, "sudo") + scriptMetadata.RequiresInteractive = strings.Contains(line, "interactive") } } @@ -245,7 +247,7 @@ func getScriptMetadata(scriptPath string) (string, bool) { desc = "No description" } - return desc, requiresSudo + return desc, scriptMetadata } func findScripts(filters []string) ([]Script, error) { @@ -271,14 +273,15 @@ func findScripts(filters []string) ([]Script, error) { scriptName := filepath.Base(path) if len(filters) == 0 || matchesFilters(relPath, scriptName, filters) { - desc, requiresSudo := getScriptMetadata(path) + desc, metaData := getScriptMetadata(path) scripts = append(scripts, Script{ - Path: path, - Name: scriptName, - RelPath: relPath, - Desc: desc, - RequiresSudo: requiresSudo, + Path: path, + Name: scriptName, + RelPath: relPath, + Desc: desc, + RequiresSudo: metaData.RequiresSudo, + RequiresInteractive: metaData.RequiresInteractive, }) } } @@ -331,14 +334,15 @@ func executeScript(script Script, args []string, verbose bool) error { errorLog("sudo command not found - cannot run elevated script") return fmt.Errorf("sudo not available") } - fullArgs := append([]string{script.Path}, args...) + // Use -S to read password from stdin, and preserve environment + fullArgs := append([]string{"-S", "-E", script.Path}, args...) cmd = exec.Command("sudo", fullArgs...) sudoLog("Running with sudo: %s", strings.Join(append([]string{script.Path}, args...), " ")) } else { cmd = exec.Command(script.Path, args...) } - if config.Interactive { + if config.Interactive || script.RequiresInteractive { cmd.Stdin = os.Stdin } @@ -348,7 +352,7 @@ func executeScript(script Script, args []string, verbose bool) error { } defer logFileHandle.Close() - showOutput := verbose || config.Interactive + showOutput := verbose || config.Interactive || script.RequiresInteractive if showOutput { cmd.Stdout = io.MultiWriter(os.Stdout, logFileHandle) cmd.Stderr = io.MultiWriter(os.Stderr, logFileHandle) @@ -371,6 +375,7 @@ func createNewScript(scriptName string) error { if !strings.HasSuffix(scriptName, ".sh") { scriptName += ".sh" + scriptPath = filepath.Join(config.RunsDir, scriptName) } if _, err := os.Stat(scriptPath); err == nil { @@ -378,15 +383,52 @@ func createNewScript(scriptName string) error { } template := fmt.Sprintf(`#!/usr/bin/env bash -# NAME: %s script -# REQUIRES: sudo +# NAME: %s +# REQUIRES: sudo interactive set -euo pipefail -echo "Running %s script..." +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} -echo "✅ %s completed successfully" -`, strings.TrimSuffix(scriptName, ".sh"), scriptName, strings.TrimSuffix(scriptName, ".sh")) +check_requirements() { + # Check required commands + local required_commands=() + # Add your required commands here + # required_commands=(git curl wget) + + for cmd in "${required_commands[@]}"; do + if ! command_exists "$cmd"; then + log_error "Required command not found: $cmd" + exit 1 + fi + done +} + +main() { + init_script + + check_requirements + + # Your main script logic goes here + + + # Example operations: + # if ! confirm "Proceed with operation?"; then + # log_info "Operation cancelled" + # finish_script 0 + # fi + + # Add your implementation here + + finish_script 0 +} + +main "$@" +`, strings.TrimSuffix(filepath.Base(scriptName), ".sh")) err := os.WriteFile(scriptPath, []byte(template), 0o755) if err != nil { diff --git a/runs/aur b/runs/aur index 9805ee6..2724369 100755 --- a/runs/aur +++ b/runs/aur @@ -1,34 +1,62 @@ #!/usr/bin/env bash -# NAME: aur script +# NAME: Install AUR packages set -euo pipefail -echo "Running aur.sh script..." +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} -paru -S --needed --noconfirm adwaita-dark \ - aws-session-manager-plugin \ - bottles \ - bruno-bin \ - davmail \ - dmg2img \ - ebgaramond-otf \ - insomnia-bin \ - kubefwd-bin \ - mpvpaper \ - ngrok \ - openvpn3-git \ - otf-font-awesome-5 \ - postman-bin \ - rofi-lbonn-wayland-git \ - rose-pine-gtk-theme \ - slack-desktop \ - soapui \ - sublime-merge \ - swayfx-git \ - teams-for-linux-bin \ - ttf-font-awesome-5 \ - ttf-ms-win11-auto \ - watchman-bin \ - yubico-authenticator-bin +check_requirements() { + if ! command_exists paru; then + log_error "paru not found. Install it first" + exit 1 + fi +} -echo "✅ aur completed successfully" +install_aur_packages() { + local packages=( + adwaita-dark + aws-session-manager-plugin + bottles + bruno-bin + davmail + dmg2img + ebgaramond-otf + insomnia-bin + kubefwd-bin + mpvpaper + ngrok + openvpn3-git + otf-font-awesome-5 + postman-bin + rofi-lbonn-wayland-git + rose-pine-gtk-theme + slack-desktop + soapui + sublime-merge + swayfx-git + teams-for-linux-bin + ttf-font-awesome-5 + ttf-ms-win11-auto + watchman-bin + yubico-authenticator-bin + betterbird-bin + ) + + log_info "Installing AUR packages" + paru -S --needed --noconfirm "${packages[@]}" +} + +main() { + init_script + + check_requirements + install_aur_packages + + finish_script 0 +} + +main "$@" diff --git a/runs/base b/runs/base index dfc42b1..da60b0f 100755 --- a/runs/base +++ b/runs/base @@ -1,199 +1,237 @@ #!/usr/bin/env bash -# NAME: Install base system packages +# NAME: Install base system packages with hardware detection set -euo pipefail -AUR_HELPER="paru" - -if ! command -v $AUR_HELPER &>/dev/null; then - echo "[ERROR] $AUR_HELPER not found. Install it first." +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 exit 1 -fi +} -echo "Installing organized system packages..." +check_requirements() { + if ! command_exists paru; then + log_error "paru not found. Install it first" + exit 1 + fi +} -# ═══════════════════════════════════════════════════════════ -# 🔧 DETECT HARDWARE -# ═══════════════════════════════════════════════════════════ -CPU_VENDOR=$(lscpu | grep "Vendor ID" | awk '{print $3}') -GPU_INFO=$(lspci | grep -i vga) +detect_hardware() { + local cpu_vendor gpu_info microcode gpu_packages -echo "Detected CPU: $CPU_VENDOR" -echo "Detected GPU: $GPU_INFO" + cpu_vendor=$(lscpu | grep "Vendor ID" | awk '{print $3}') + gpu_info=$(lspci | grep -i vga) -# Determine microcode package -if [[ "$CPU_VENDOR" == "GenuineIntel" ]]; then - MICROCODE="intel-ucode" - echo "→ Using Intel microcode" -elif [[ "$CPU_VENDOR" == "AuthenticAMD" ]]; then - MICROCODE="amd-ucode" - echo "→ Using AMD microcode" -else - MICROCODE="" - echo "→ Unknown CPU vendor, skipping microcode" -fi + log_info "Detected CPU: $cpu_vendor" + log_info "Detected GPU: $gpu_info" -# Determine GPU drivers -GPU_PACKAGES="" -if echo "$GPU_INFO" | grep -qi "amd\|radeon"; then - GPU_PACKAGES="xf86-video-amdgpu vulkan-radeon lib32-vulkan-radeon opencl-mesa lib32-opencl-mesa" - echo "→ Using AMD GPU drivers" -elif echo "$GPU_INFO" | grep -qi "intel"; then - GPU_PACKAGES="xf86-video-intel vulkan-intel lib32-vulkan-intel intel-media-driver" - echo "→ Using Intel GPU drivers" -elif echo "$GPU_INFO" | grep -qi "nvidia"; then - GPU_PACKAGES="nvidia nvidia-utils lib32-nvidia-utils nvidia-settings" - echo "→ Using NVIDIA drivers (you may want to review this)" -else - echo "→ Unknown GPU, using generic drivers" -fi + # Determine microcode package + case "$cpu_vendor" in + "GenuineIntel") + microcode="intel-ucode" + log_info "Using Intel microcode" + ;; + "AuthenticAMD") + microcode="amd-ucode" + log_info "Using AMD microcode" + ;; + *) + microcode="" + log_warn "Unknown CPU vendor, skipping microcode" + ;; + esac -# ═══════════════════════════════════════════════════════════ -# 🔧 SYSTEM BASE -# ═══════════════════════════════════════════════════════════ -echo "Installing system base..." -BASE_PACKAGES="base base-devel sudo stow linux-cachyos linux-cachyos-headers linux-firmware efibootmgr efitools mkinitcpio grub systemd-boot-manager" + # Determine GPU drivers + gpu_packages="" + if echo "$gpu_info" | grep -qi "amd\|radeon"; then + gpu_packages="xf86-video-amdgpu vulkan-radeon lib32-vulkan-radeon opencl-mesa lib32-opencl-mesa" + log_info "Using AMD GPU drivers" + elif echo "$gpu_info" | grep -qi "intel"; then + gpu_packages="xf86-video-intel vulkan-intel lib32-vulkan-intel intel-media-driver" + log_info "Using Intel GPU drivers" + elif echo "$gpu_info" | grep -qi "nvidia"; then + gpu_packages="nvidia nvidia-utils lib32-nvidia-utils nvidia-settings" + log_info "Using NVIDIA drivers" + else + log_warn "Unknown GPU, using generic drivers" + fi +} -if [[ -n "$MICROCODE" ]]; then - BASE_PACKAGES="$BASE_PACKAGES $MICROCODE" -fi +install_base_system() { + log_info "Installing system base packages" -$AUR_HELPER -S --noconfirm --needed $BASE_PACKAGES + local base_packages="base base-devel sudo stow linux-cachyos linux-cachyos-headers linux-firmware efibootmgr efitools mkinitcpio grub systemd-boot-manager" -# ═══════════════════════════════════════════════════════════ -# 🌐 NETWORKING & BLUETOOTH -# ═══════════════════════════════════════════════════════════ -echo "Installing networking..." -$AUR_HELPER -S --noconfirm --needed \ - networkmanager networkmanager-openvpn network-manager-applet \ - dhclient dnsmasq iptables-nft iwd wpa_supplicant wireless-regdb \ - bluez-libs blueman openssh + if [[ -n "$microcode" ]]; then + base_packages="$base_packages $microcode" + fi -# ═══════════════════════════════════════════════════════════ -# 🔊 AUDIO & VIDEO -# ═══════════════════════════════════════════════════════════ -echo "Installing audio/video..." -$AUR_HELPER -S --noconfirm --needed \ - pipewire pipewire-alsa pipewire-pulse wireplumber \ - pavucontrol alsa-firmware alsa-plugins alsa-utils \ - gst-libav gst-plugin-pipewire gst-plugins-bad gst-plugins-ugly \ - mpv ffmpegthumbnailer + paru -S --needed --overwrite="*" $base_packages +} -# ═══════════════════════════════════════════════════════════ -# 🎨 FONTS & THEMES -# ═══════════════════════════════════════════════════════════ -echo "Installing fonts and themes..." -$AUR_HELPER -S --noconfirm --needed \ - adobe-source-han-sans-cn-fonts adobe-source-han-sans-jp-fonts \ - adobe-source-han-sans-kr-fonts adobe-source-serif-fonts \ - awesome-terminal-fonts inter-font noto-fonts noto-fonts-cjk noto-fonts-emoji \ - ttf-bitstream-vera ttf-dejavu ttf-fantasque-nerd ttf-fira-code ttf-fira-mono \ - ttf-fira-sans ttf-font-awesome-5 ttf-liberation ttf-linux-libertine \ - ttf-meslo-nerd ttf-ms-win11-auto ttf-opensans otf-font-awesome-5 otf-libertinus \ - papirus-icon-theme rose-pine-gtk-theme +install_networking() { + log_info "Installing networking packages" -# ═══════════════════════════════════════════════════════════ -# 💻 DEVELOPMENT TOOLS -# ═══════════════════════════════════════════════════════════ -echo "Installing development tools..." -$AUR_HELPER -S --noconfirm --needed \ - git git-lfs git-crypt github-cli lazygit \ - go go-task python python-defusedxml python-packaging \ - python-protobuf python-pynvim python-pywlroots lua luarocks \ - ccls cmake ninja neovim-nightly-bin + paru -S --needed \ + networkmanager networkmanager-openvpn network-manager-applet \ + dhclient dnsmasq iptables-nft iwd wpa_supplicant wireless-regdb \ + bluez-libs blueman openssh +} -# ═══════════════════════════════════════════════════════════ -# 🛠️ CLI UTILITIES -# ═══════════════════════════════════════════════════════════ -echo "Installing CLI utilities..." -$AUR_HELPER -S --noconfirm --needed \ - bat btop duf dysk dust eza fd fzf glances glow httpie ncdu \ - plocate ripgrep tealdeer the_silver_searcher tmux wget \ - xdg-user-dirs zoxide zellij yazi yq zsh +install_audio_video() { + log_info "Installing audio and video packages" -# ═══════════════════════════════════════════════════════════ -# 🪟 WINDOW MANAGERS & DESKTOP -# ═══════════════════════════════════════════════════════════ -echo "Installing window managers..." -$AUR_HELPER -S --noconfirm --needed \ - swaybg swayfx-git swayidle swaylock-effects waybar \ - wlogout wdisplays wl-clipboard rofi-lbonn-wayland-git + paru -S --needed \ + pipewire pipewire-alsa pipewire-pulse wireplumber \ + pavucontrol alsa-firmware alsa-plugins alsa-utils \ + gst-libav gst-plugin-pipewire gst-plugins-bad gst-plugins-ugly \ + mpv ffmpegthumbnailer +} -# ═══════════════════════════════════════════════════════════ -# 🌍 GUI APPLICATIONS -# ═══════════════════════════════════════════════════════════ -echo "Installing GUI applications..." -$AUR_HELPER -S --noconfirm --needed \ - chromium zen-browser-bin \ - legcord-bin slack-desktop teams-for-linux-bin \ - obs-studio obsidian sublime-merge dbeaver \ - libreoffice-fresh kitty nemo nemo-fileroller +install_fonts_themes() { + log_info "Installing fonts and themes" -# ═══════════════════════════════════════════════════════════ -# 📁 SYSTEM UTILITIES -# ═══════════════════════════════════════════════════════════ -echo "Installing system utilities..." -$AUR_HELPER -S --noconfirm --needed \ - brightnessctl cronie cups cups-pdf gamemode gvfs gvfs-afc gvfs-google \ - gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb haveged hdparm less lvm2 \ - man-db man-pages meld modemmanager mtools mupdf netctl nss-mdns \ - ntfs-3g ntp nvme-cli opencl-mesa pacman-contrib pass pika-backup \ - pkgfile power-profiles-daemon pv reflector remmina rsync rtkit \ - samba seahorse sg3_utils smartmontools snapper solaar steam \ - steam-native-runtime syncthing system-config-printer timeshift \ - transmission-qt ufw unrar unzip upower usb_modeswitch usbutils \ - vi vorta w3m which xdg-desktop-portal xdg-desktop-portal-gnome \ - xdg-desktop-portal-gtk xdg-desktop-portal-wlr yad zenity + paru -S --needed \ + adobe-source-han-sans-cn-fonts adobe-source-han-sans-jp-fonts \ + adobe-source-han-sans-kr-fonts adobe-source-serif-fonts \ + awesome-terminal-fonts inter-font noto-fonts noto-fonts-cjk noto-fonts-emoji \ + ttf-bitstream-vera ttf-dejavu ttf-fantasque-nerd ttf-fira-code ttf-fira-mono \ + ttf-fira-sans ttf-font-awesome-5 ttf-liberation ttf-linux-libertine \ + ttf-meslo-nerd ttf-ms-win11-auto ttf-opensans otf-font-awesome-5 otf-libertinus \ + papirus-icon-theme rose-pine-gtk-theme +} -# ═══════════════════════════════════════════════════════════ -# 🎮 GAMING & GRAPHICS -# ═══════════════════════════════════════════════════════════ -echo "Installing gaming and graphics..." -GAMING_BASE="lib32-alsa-lib lib32-alsa-plugins lib32-gamemode lib32-libpulse lib32-mesa lib32-openal lib32-vkd3d lib32-vulkan-mesa-layers vkd3d vulkan-mesa-layers vulkan-tools vulkan-validation-layers wine protonup-qt" +install_development() { + log_info "Installing development tools" -if [[ -n "$GPU_PACKAGES" ]]; then - $AUR_HELPER -S --noconfirm --needed $GAMING_BASE $GPU_PACKAGES -else - $AUR_HELPER -S --noconfirm --needed $GAMING_BASE -fi + paru -S --needed \ + git git-lfs git-crypt github-cli lazygit \ + go go-task python python-defusedxml python-packaging \ + python-protobuf python-pynvim python-pywlroots lua luarocks \ + ccls cmake ninja neovim-nightly-bin +} -# ═══════════════════════════════════════════════════════════ -# 📱 MOBILE & HARDWARE -# ═══════════════════════════════════════════════════════════ -echo "Installing hardware support..." -$AUR_HELPER -S --noconfirm --needed \ - sof-firmware xf86-input-libinput xfsprogs \ - xorg-server xorg-xdpyinfo xorg-xhost xorg-xinit xorg-xinput \ - xorg-xkill xorg-xrandr xorg-xwayland +install_cli_utilities() { + log_info "Installing CLI utilities" -# ═══════════════════════════════════════════════════════════ -# ☁️ CLOUD & DEVOPS -# ═══════════════════════════════════════════════════════════ -echo "Installing cloud and DevOps tools..." -$AUR_HELPER -S --noconfirm --needed \ - aws-cli aws-session-manager-plugin insomnia-bin \ - kubectl kubectx kubefwd-bin ngrok postgresql + paru -S --needed \ + bat btop duf dysk dust eza fd fzf glances glow httpie ncdu \ + plocate ripgrep tealdeer the_silver_searcher tmux wget \ + xdg-user-dirs zoxide yazi yq zsh +} -# ═══════════════════════════════════════════════════════════ -# 📚 DOCUMENT & PRODUCTIVITY -# ═══════════════════════════════════════════════════════════ -echo "Installing document tools..." -$AUR_HELPER -S --noconfirm --needed \ - pandoc-cli texinfo wkhtmltopdf-bin zathura zathura-pdf-mupdf \ - swappy wf-recorder +install_window_managers() { + log_info "Installing window managers and desktop environment" -# ═══════════════════════════════════════════════════════════ -# 🔐 SECURITY & ENCRYPTION -# ═══════════════════════════════════════════════════════════ -echo "Installing security tools..." -$AUR_HELPER -S --noconfirm --needed \ - yubico-authenticator-bin yubikey-manager-qt + paru -S --needed \ + swaybg swayfx-git swayidle swaylock-effects waybar \ + wlogout wdisplays wl-clipboard rofi-lbonn-wayland-git +} -# ═══════════════════════════════════════════════════════════ -# 📦 PACKAGE MANAGERS & MISC -# ═══════════════════════════════════════════════════════════ -echo "Installing package managers and misc..." -$AUR_HELPER -S --noconfirm --needed \ - pyenv watchman-bin wimlib +install_gui_applications() { + log_info "Installing GUI applications" -tldr --update + paru -S --needed \ + chromium zen-browser-bin \ + legcord-bin slack-desktop teams-for-linux-bin \ + obs-studio obsidian sublime-merge dbeaver \ + libreoffice-fresh kitty nemo nemo-fileroller +} + +install_system_utilities() { + log_info "Installing system utilities" + + paru -S --needed \ + brightnessctl cronie cups cups-pdf gamemode gvfs gvfs-afc gvfs-google \ + gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb haveged hdparm less lvm2 \ + man-db man-pages meld modemmanager mtools mupdf netctl nss-mdns \ + ntfs-3g ntp nvme-cli opencl-mesa pacman-contrib pass pika-backup \ + pkgfile power-profiles-daemon pv reflector remmina rsync rtkit \ + samba seahorse sg3_utils smartmontools snapper solaar steam \ + steam-native-runtime syncthing system-config-printer \ + transmission-qt ufw unrar unzip upower usb_modeswitch usbutils \ + vi vorta w3m which xdg-desktop-portal xdg-desktop-portal-gnome \ + xdg-desktop-portal-gtk xdg-desktop-portal-wlr yad zenity +} + +install_gaming_graphics() { + log_info "Installing gaming and graphics packages" + + local gaming_base="lib32-alsa-lib lib32-alsa-plugins lib32-gamemode lib32-libpulse lib32-mesa lib32-openal lib32-vkd3d lib32-vulkan-mesa-layers vkd3d vulkan-mesa-layers vulkan-tools vulkan-validation-layers wine protonup-qt" + + if [[ -n "$gpu_packages" ]]; then + paru -S --needed $gaming_base $gpu_packages + else + paru -S --needed $gaming_base + fi +} + +install_hardware_support() { + log_info "Installing hardware support" + + paru -S --needed \ + sof-firmware xf86-input-libinput xfsprogs \ + xorg-server xorg-xdpyinfo xorg-xhost xorg-xinit xorg-xinput \ + xorg-xkill xorg-xrandr xorg-xwayland +} + +install_cloud_devops() { + log_info "Installing cloud and DevOps tools" + + paru -S --needed \ + aws-cli aws-session-manager-plugin insomnia-bin \ + kubectl kubectx kubefwd-bin ngrok postgresql +} + +install_document_tools() { + log_info "Installing document and productivity tools" + + paru -S --needed \ + pandoc-cli texinfo wkhtmltopdf-bin zathura zathura-pdf-mupdf \ + swappy wf-recorder +} + +install_security_tools() { + log_info "Installing security tools" + + paru -S --needed \ + yubico-authenticator-bin yubikey-manager-qt +} + +install_misc_packages() { + log_info "Installing miscellaneous packages" + + paru -S --needed \ + pyenv watchman-bin wimlib \ + slurp grim swaync gum nwg-look lxsession colordiff + + log_info "Updating tldr database" + tldr --update +} + +main() { + init_script + + check_requirements + detect_hardware + + install_base_system + install_networking + install_audio_video + install_fonts_themes + install_development + install_cli_utilities + install_window_managers + install_gui_applications + install_system_utilities + install_gaming_graphics + install_hardware_support + install_cloud_devops + install_document_tools + install_security_tools + install_misc_packages + + finish_script 0 +} + +main "$@" diff --git a/runs/boot b/runs/boot index 06daaf3..74f1034 100755 --- a/runs/boot +++ b/runs/boot @@ -1,37 +1,40 @@ #!/usr/bin/env bash # NAME: Configure systemd-boot manager settings +# REQUIRES: sudo set -euo pipefail -SDBOOT_CONFIG="/etc/sdboot-manage.conf" - -echo "Configuring systemd-boot manager..." - -# Check if sdboot-manage is installed -if ! command -v sdboot-manage &>/dev/null; then - echo "⚠️ sdboot-manage not found. Install systemd-boot-manager first." +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 exit 1 -fi +} -# Backup existing config if it exists -if [[ -f "$SDBOOT_CONFIG" ]]; then - sudo cp "$SDBOOT_CONFIG" "$SDBOOT_CONFIG.backup.$(date +%Y%m%d_%H%M%S)" - echo "→ Backed up existing config" -fi +readonly SDBOOT_CONFIG="/etc/sdboot-manage.conf" -# Create the configuration -sudo tee "$SDBOOT_CONFIG" >/dev/null <<'EOF' +check_requirements() { + if ! command_exists sdboot-manage; then + log_error "sdboot-manage not found. Install systemd-boot-manager first" + exit 1 + fi +} + +configure_systemd_boot() { + log_info "Configuring systemd-boot manager" + + if [[ -f "$SDBOOT_CONFIG" ]]; then + backup_file "$SDBOOT_CONFIG" + fi + + log_info "Creating systemd-boot configuration" + tee "$SDBOOT_CONFIG" >/dev/null <<'EOF' # Config file for sdboot-manage # Kernel options to be appended to the "options" line LINUX_OPTIONS="zswap.enabled=0 nowatchdog ipv6.disable=1 audit=0 loglevel=3 rd.systemd.show_status=auto rd.udev.log_level=3" -#LINUX_FALLBACK_OPTIONS="" # When DISABLE_FALLBACK is set to "yes", it will stop creating fallback entries for each kernel. DISABLE_FALLBACK="no" -# Use this pattern to match kernels which should be considered native OS kernels -#KERNEL_PATTERN="vmlinuz-" - # Setting REMOVE_EXISTING to "yes" will remove all your existing systemd-boot entries before building new entries REMOVE_EXISTING="yes" @@ -41,19 +44,18 @@ OVERWRITE_EXISTING="yes" # When REMOVE_OBSOLETE is set to "yes" entries for kernels no longer available on the system will be removed REMOVE_OBSOLETE="yes" - -# If PRESERVE_FOREIGN is set to "yes", do not delete entries starting with $ENTRY_ROOT -#PRESERVE_FOREIGN="no" - -# Setting NO_AUTOUPDATE to "yes" will stop the updates to systemd-boot when systemd is updated - not recommended unless you are seperately updating systemd-boot -#NO_AUTOUPDATE="no" - -# Setting NO_AUTOGEN to "yes" will stop the automatic creation of entries when kernels are installed or updated -#NO_AUTOGEN="no" - -# Use this to change the default initramfs prefix (e.g. when using booster) -#INITRD_PREFIX="initramfs" - -# Additional entities to initrd can be added here, such as microcode if your initramfs does not include it. -#INITRD_ENTRIES=() EOF + + log_info "systemd-boot configuration updated" +} + +main() { + init_script + + check_requirements + configure_systemd_boot + + finish_script 0 +} + +main "$@" diff --git a/runs/docker b/runs/docker index f209c60..3d99b10 100755 --- a/runs/docker +++ b/runs/docker @@ -1,24 +1,41 @@ #!/usr/bin/env bash -# NAME: Install and configure Docker on Arch Linux +# NAME: Install and configure Docker set -euo pipefail -echo "Installing Docker..." +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} -# Install Docker packages -sudo pacman -S --needed --noconfirm docker docker-compose docker-buildx +install_docker() { + log_info "Installing Docker packages" + sudo pacman -S --needed --noconfirm docker docker-compose docker-buildx +} -# Add current user to docker group -sudo usermod -aG docker "$USER" +configure_docker() { + log_info "Adding current user to docker group" + sudo usermod -aG docker "$USER" -# Enable and start Docker service -sudo systemctl enable docker.service -sudo systemctl start docker.service + log_info "Enabling Docker services" + sudo systemctl enable docker.service + sudo systemctl start docker.service + sudo systemctl enable containerd.service + sudo systemctl start containerd.service +} -# Enable containerd service (dependency) -sudo systemctl enable containerd.service -sudo systemctl start containerd.service +main() { + init_script -echo "✅ Docker installed successfully!" -echo "⚠️ You need to log out and back in (or reboot) for group changes to take effect" -echo "🐳 Test with: docker run hello-world" + install_docker + configure_docker + + log_info "Docker installed successfully" + log_warn "You need to log out and back in (or reboot) for group changes to take effect" + log_info "Test with: docker run hello-world" + + finish_script 0 +} + +main "$@" diff --git a/runs/dotfiles b/runs/dotfiles index fcb41d2..02de3e2 100755 --- a/runs/dotfiles +++ b/runs/dotfiles @@ -1,151 +1,118 @@ #!/usr/bin/env bash -# NAME: Setup dotfiles and Neovim configuration +# NAME: Setup dotfiles and configuration +# REQUIRES: interactive set -euo pipefail -DOTFILES_REPO="https://git.mz.uy/marianozunino/dotfiles.git" -NVIM_REPO="https://github.com/marianozunino/nvim.git" -DOTFILES_DIR="$HOME/dotfiles" -NVIM_CONFIG_DIR="$HOME/.config/nvim" +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} -echo "🏠 Setting up Forbi's development environment..." +readonly DOTFILES_REPO="https://git.mz.uy/marianozunino/dotfiles.git" +readonly DOTFILES_DIR="$HOME/dotfiles" -# ═══════════════════════════════════════════════════════════ -# 📁 CLONE DOTFILES -# ═══════════════════════════════════════════════════════════ -echo "📦 Cloning dotfiles repository..." +check_requirements() { + local required_commands=(git git-crypt stow) -if [[ -d "$DOTFILES_DIR" ]]; then - echo "→ Dotfiles directory already exists, updating..." - cd "$DOTFILES_DIR" - git pull origin main || git pull origin master -else - echo "→ Cloning dotfiles to $DOTFILES_DIR" - git clone "$DOTFILES_REPO" "$DOTFILES_DIR" -fi - -# ═══════════════════════════════════════════════════════════ -# 🔐 HANDLE GIT-CRYPT -# ═══════════════════════════════════════════════════════════ -echo "🔐 Checking for git-crypt..." - -cd "$DOTFILES_DIR" - -# Check if repo uses git-crypt -if [[ -f ".git-crypt/.gitattributes" ]] || git-crypt status &>/dev/null; then - echo "→ git-crypt repository detected" - - # Check if already unlocked - if git-crypt status | grep -q "unlocked"; then - echo "→ Repository already unlocked" - else - echo "→ Repository is encrypted, attempting to unlock..." - - # Check if YubiKey/GPG setup is available - if command -v gpg &>/dev/null; then - echo "→ GPG found, checking for available keys..." - - # List secret keys to see if YubiKey is connected - if gpg --list-secret-keys &>/dev/null; then - echo "→ GPG keys detected, attempting git-crypt unlock..." - - # Attempt to unlock with GPG (YubiKey) - if git-crypt unlock 2>/dev/null; then - echo "✅ Repository unlocked successfully with GPG/YubiKey!" - else - echo "❌ Failed to unlock with GPG" - echo - echo "🔑 Manual unlock required:" - echo " 1. Make sure your YubiKey is connected" - echo " 2. Test GPG: gpg --card-status" - echo " 3. Try: cd $DOTFILES_DIR && git-crypt unlock" - echo " 4. Or unlock with key file: git-crypt unlock /path/to/key" - echo " 5. Then re-run: ./run forbi" - echo - exit 1 - fi - else - echo "❌ No GPG secret keys found" - echo - echo "🔑 YubiKey/GPG setup needed:" - echo " 1. Connect your YubiKey" - echo " 2. Import your GPG key: gpg --card-status" - echo " 3. Set trust level: gpg --edit-key trust" - echo " 4. Then re-run: ./run forbi" - echo - exit 1 - fi - else - echo "❌ GPG not available" - echo - echo "🔑 To unlock your dotfiles:" - echo " 1. Install GPG and connect YubiKey, or" - echo " 2. Use key file: cd $DOTFILES_DIR && git-crypt unlock /path/to/key" - echo " 3. Then re-run: ./run forbi" - echo + for cmd in "${required_commands[@]}"; do + if ! command_exists "$cmd"; then + log_error "Required command not found: $cmd" exit 1 fi + done +} + +clone_dotfiles() { + log_info "Setting up dotfiles repository" + + if [[ -d "$DOTFILES_DIR" ]]; then + log_info "Dotfiles directory already exists, updating" + cd "$DOTFILES_DIR" + git pull origin main || git pull origin master + else + log_info "Cloning dotfiles to $DOTFILES_DIR" + git clone "$DOTFILES_REPO" "$DOTFILES_DIR" fi -else - echo "→ No git-crypt encryption detected" -fi +} -# ═══════════════════════════════════════════════════════════ -# ⚙️ CLONE NEOVIM CONFIG -# ═══════════════════════════════════════════════════════════ -echo "📝 Setting up Neovim configuration..." +handle_git_crypt() { + log_info "Checking git-crypt status" + cd "$DOTFILES_DIR" -# Backup existing Neovim config if it exists -if [[ -d "$NVIM_CONFIG_DIR" ]]; then - BACKUP_DIR="$NVIM_CONFIG_DIR.backup.$(date +%Y%m%d_%H%M%S)" - echo "→ Backing up existing Neovim config to $BACKUP_DIR" - mv "$NVIM_CONFIG_DIR" "$BACKUP_DIR" -fi + if git-crypt status | grep -q "unlocked"; then + log_info "Repository already unlocked" + return 0 + fi -# Clone Neovim configuration -echo "→ Cloning Neovim config to $NVIM_CONFIG_DIR" -git clone "$NVIM_REPO" "$NVIM_CONFIG_DIR" + log_info "Repository is encrypted, attempting to unlock" -# ═══════════════════════════════════════════════════════════ -# 🔧 CONFIGURE GIT HOOKS -# ═══════════════════════════════════════════════════════════ -echo "🎣 Setting up Git hooks for Neovim config..." + if gpg --card-status &>/dev/null; then + log_info "YubiKey detected, fetching public key" -cd "$NVIM_CONFIG_DIR" -if [[ -d ".githooks" ]]; then - git config core.hooksPath .githooks - echo "→ Git hooks configured" -else - echo "→ No .githooks directory found, skipping" -fi + local key_id + key_id=$(gpg --card-status 2>/dev/null | grep "General key info" -A 1 | tail -1 | awk '{print $1}' | sed 's/.*\///') -# ═══════════════════════════════════════════════════════════ -# 📂 CREATE NECESSARY DIRECTORIES -# ═══════════════════════════════════════════════════════════ -echo "📂 Creating necessary directories..." + if [[ -n "$key_id" ]] && ! gpg --list-keys "$key_id" &>/dev/null; then + gpg --card-edit --batch --command-fd 0 <<<"fetch" &>/dev/null || + gpg --keyserver hkps://keys.openpgp.org --recv-keys "$key_id" &>/dev/null || true + fi + else + log_error "YubiKey not detected" + exit 1 + fi -# Create .config directory if it doesn't exist -mkdir -p "$HOME/.config" + if git-crypt unlock 2>/dev/null; then + log_info "Repository unlocked successfully" + else + log_error "Failed to unlock repository" + log_info "Manual unlock required: cd $DOTFILES_DIR && git-crypt unlock" + exit 1 + fi +} -# Create common development directories -mkdir -p "$HOME/Dev" -mkdir -p "$HOME/.local/bin" +create_directories() { + log_info "Creating necessary directories" + mkdir -p "$HOME/.config" "$HOME/Dev" "$HOME/.local/bin" +} -echo -echo "# Install everything" -echo "stow */" -echo -echo "# Or install individual modules" -echo "stow zsh" -echo "stow nvim" -echo "stow sway" -echo -echo "Run these commands in: $DOTFILES_DIR" +stow_packages() { + cd "$DOTFILES_DIR" -# ═══════════════════════════════════════════════════════════ -# 📋 SUMMARY & NEXT STEPS -# ═══════════════════════════════════════════════════════════ -echo -echo "📁 Locations:" -echo " • Dotfiles: $DOTFILES_DIR" -echo " • Neovim config: $NVIM_CONFIG_DIR" + log_info "Available stow packages:" + for dir in */; do + if [[ -d "$dir" ]]; then + log_info " ${dir%/}" + fi + done + + if confirm "Stow all packages?" "y"; then + log_info "Stowing all packages" + if stow */; then + log_info "All packages stowed successfully" + else + log_error "Some packages failed to stow. Check for conflicts" + fi + else + log_info "Skipping automatic stow. Run manually:" + log_info " cd $DOTFILES_DIR" + log_info " stow " + fi +} + +main() { + init_script + + check_requirements + clone_dotfiles + handle_git_crypt + create_directories + stow_packages + + log_info "Dotfiles location: $DOTFILES_DIR" + + finish_script 0 +} + +main "$@" diff --git a/runs/limine b/runs/limine deleted file mode 100755 index 8732759..0000000 --- a/runs/limine +++ /dev/null @@ -1,211 +0,0 @@ -#!/bin/bash -# NAME: Script to transition from systemd-boot to Limine bootloader -# REQUIRES: sudo - -set -euo pipefail - -echo "do you confirm this transition?" - -read -r -p "Are you sure? [y/N] " response - -if [[ "$response" != "y" ]]; then - exit 0 -fi - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -print_status() { echo -e "${BLUE}[INFO]${NC} $1"; } -print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } -print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } -print_error() { echo -e "${RED}[ERROR]${NC} $1"; } - -if [[ $EUID -ne 0 ]]; then - print_error "This script must be run as root (use sudo)" - exit 1 -fi - -if ! command -v pacman &>/dev/null; then - print_error "This script is designed for Arch Linux / CachyOS systems" - exit 1 -fi - -print_status "Starting transition from systemd-boot to Limine..." - -ESP_PATH="" -if command -v bootctl &>/dev/null; then - ESP_PATH=$(bootctl --print-esp-path 2>/dev/null || echo "") -fi - -if [[ -z "$ESP_PATH" ]]; then - if [[ -d "/boot/EFI" ]]; then - ESP_PATH="/boot" - elif [[ -d "/efi/EFI" ]]; then - ESP_PATH="/efi" - else - print_error "Could not detect ESP path" - exit 1 - fi -fi - -print_success "ESP detected at: $ESP_PATH" - -BACKUP_DIR="/root/bootloader-backup-$(date +%Y%m%d-%H%M%S)" -mkdir -p "$BACKUP_DIR" - -[[ -d "$ESP_PATH/loader" ]] && cp -r "$ESP_PATH/loader" "$BACKUP_DIR/" 2>/dev/null || true -[[ -d "$ESP_PATH/EFI/systemd" ]] && cp -r "$ESP_PATH/EFI/systemd" "$BACKUP_DIR/" 2>/dev/null || true -efibootmgr -v >"$BACKUP_DIR/efibootmgr-backup.txt" 2>/dev/null || true - -print_success "Backup created at: $BACKUP_DIR" - -CURRENT_CMDLINE="" -if [[ -f "/etc/kernel/cmdline" ]]; then - CURRENT_CMDLINE=$(cat /etc/kernel/cmdline) - print_status "Found existing /etc/kernel/cmdline: $CURRENT_CMDLINE" -else - CURRENT_CMDLINE=$(cat /proc/cmdline | sed 's/BOOT_IMAGE=[^ ]* //') - print_status "Using current /proc/cmdline: $CURRENT_CMDLINE" -fi - -print_status "Installing Limine and related packages..." -pacman -S --needed --noconfirm limine limine-snapper-sync limine-mkinitcpio-hook -print_success "Limine packages installed" - -print_status "Installing Limine bootloader to ESP..." -mkdir -p "$ESP_PATH/EFI/Limine" - -if [[ -f "/usr/share/limine/limine_x64.efi" ]]; then - cp /usr/share/limine/limine_x64.efi "$ESP_PATH/EFI/Limine/" -elif [[ -f "/usr/share/limine/BOOTX64.EFI" ]]; then - cp /usr/share/limine/BOOTX64.EFI "$ESP_PATH/EFI/Limine/limine_x64.efi" -else - print_error "Could not find Limine EFI files" - exit 1 -fi - -print_success "Limine files installed to ESP" - -print_status "Creating UEFI boot entry for Limine..." -ESP_DEVICE=$(df "$ESP_PATH" | tail -1 | awk '{print $1}') -ESP_DISK=$(echo "$ESP_DEVICE" | sed 's/[0-9]*$//') -ESP_PART_NUM=$(echo "$ESP_DEVICE" | sed 's/.*[^0-9]//') - -efibootmgr -c -d "$ESP_DISK" -p "$ESP_PART_NUM" -L "Limine" -l "\\EFI\\Limine\\limine_x64.efi" || { - print_warning "Could not create UEFI boot entry automatically" -} - -print_success "Limine UEFI boot entry created" - -print_status "Configuring kernel command line..." -echo "$CURRENT_CMDLINE" >/etc/kernel/cmdline -print_success "Kernel command line configured: $CURRENT_CMDLINE" - -print_status "Configuring Limine..." -[[ ! -f "/etc/default/limine" ]] && cat >/etc/default/limine <"$ESP_PATH/limine.conf" <<'EOF' -### Read more at config document: https://github.com/limine-bootloader/limine/blob/trunk/CONFIG.md -timeout: 1 -### Note: For "default_entry" to select a sub-entry within an OS menu, modify "/OS name" to "/+OS name" to keep its submenus visible. -default_entry: 2 -#interface_branding_color: 3 -interface_branding: -hash_mismatch_panic: no -term_palette: 1e1e2e;f38ba8;a6e3a1;f9e2af;89b4fa;f5c2e7;94e2d5;cdd6f4 -term_palette_bright: 585b70;f38ba8;a6e3a1;f9e2af;89b4fa;f5c2e7;94e2d5;cdd6f4 -term_background: 1e1e2e -term_foreground: cdd6f4 -term_background_bright: 585b70 -term_foreground_bright: cdd6f4 - -EOF - -print_status "Generating Limine boot entries..." -limine-update -print_success "Limine configuration created" - -print_status "Setting Limine as default boot option..." -LIMINE_ENTRY=$(efibootmgr | grep "Limine" | head -1 | cut -c5-8) - -if [[ -n "$LIMINE_ENTRY" ]]; then - CURRENT_ORDER=$(efibootmgr | grep "BootOrder" | cut -d' ' -f2) - NEW_ORDER=$(echo "$CURRENT_ORDER" | sed "s/$LIMINE_ENTRY,//g" | sed "s/,$LIMINE_ENTRY//g" | sed "s/^$LIMINE_ENTRY$//g") - - if [[ -n "$NEW_ORDER" ]]; then - efibootmgr -o "$LIMINE_ENTRY,$NEW_ORDER" - else - efibootmgr -o "$LIMINE_ENTRY" - fi - - print_success "Limine set as default boot option" -else - print_warning "Could not find Limine boot entry to set as default" -fi - -print_status "Removing systemd-boot components..." - -# Remove systemd-boot UEFI entries -SYSTEMD_ENTRIES=$(efibootmgr | grep -i "Linux Boot Manager\|systemd" | cut -c5-8 || true) -if [[ -n "$SYSTEMD_ENTRIES" ]]; then - for entry in $SYSTEMD_ENTRIES; do - print_status "Removing UEFI boot entry: Boot$entry" - efibootmgr -b "$entry" -B || print_warning "Failed to remove boot entry $entry" - done -fi - -# Remove systemd-boot files from ESP -if [[ -d "$ESP_PATH/EFI/systemd" ]]; then - print_status "Removing systemd-boot EFI files..." - rm -rf "$ESP_PATH/EFI/systemd" - print_success "systemd-boot EFI files removed" -fi - -if [[ -d "$ESP_PATH/loader" ]]; then - print_status "Removing systemd-boot configuration..." - rm -rf "$ESP_PATH/loader" - print_success "systemd-boot configuration removed" -fi - -if [[ -f "$ESP_PATH/limine.conf" ]]; then - print_success "Limine configuration file exists" -else - print_error "Limine configuration file missing!" -fi - -if [[ -f "$ESP_PATH/EFI/Limine/limine_x64.efi" ]]; then - print_success "Limine EFI bootloader exists" -else - print_error "Limine EFI bootloader missing!" -fi - -sudo pacman -S snap-pac -sudo systemctl enable --now limine-snapper-sync.service - -echo -print_success "TRANSITION COMPLETED SUCCESSFULLY!" -echo -print_status "Summary:" -echo " • Limine bootloader installed to: $ESP_PATH/EFI/Limine/" -echo " • Configuration file: $ESP_PATH/limine.conf" -echo " • Kernel command line: /etc/kernel/cmdline" -echo " • Backup created at: $BACKUP_DIR" -echo " • Boot order updated" -echo " • systemd-boot completely removed" -echo -print_warning "IMPORTANT: Reboot to test the new Limine setup!" -print_warning "systemd-boot has been completely removed - ensure Limine works before removing backup!" -print_success "Script completed. Please reboot when ready." diff --git a/runs/limine_hibernate b/runs/limine_hibernate new file mode 100755 index 0000000..2da4470 --- /dev/null +++ b/runs/limine_hibernate @@ -0,0 +1,279 @@ +#!/usr/bin/env bash +# NAME: Configure hibernation for CachyOS with Limine bootloader +# REQUIRES: sudo interactive + +set -euo pipefail + +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} + +readonly LIMINE_CONFIG="/etc/default/limine" +readonly MKINITCPIO_CONFIG="/etc/mkinitcpio.conf" + +check_requirements() { + local swap_uuid + swap_uuid=$(blkid -t TYPE=swap -o value -s UUID | head -n1) + + if [[ -z "$swap_uuid" ]]; then + log_error "No swap partition found" + log_info "Please create a swap partition or swapfile before running this script" + exit 1 + fi + + log_info "Found swap partition UUID: $swap_uuid" + readonly SWAP_UUID="$swap_uuid" +} + +escape_for_sed() { + printf '%s\n' "$1" | sed 's/[[\.*^$()+?{|]/\\&/g' +} + +show_diff_and_confirm() { + local file="$1" + local description="$2" + local temp_file="$3" + + log_info "Proposed changes for $description" + log_info "File: $file" + echo + + if command_exists colordiff; then + colordiff -u "$file" "$temp_file" || true + else + diff -u "$file" "$temp_file" || true + fi + + echo + confirm "Apply these changes?" +} + +update_limine_config() { + log_info "Updating Limine configuration" + + backup_file "$LIMINE_CONFIG" + + local current_cmdline new_cmdline changes_made=false + current_cmdline=$(grep 'KERNEL_CMDLINE\[default\]' "$LIMINE_CONFIG" | cut -d'"' -f2) + log_info "Current kernel cmdline: $current_cmdline" + + new_cmdline="$current_cmdline" + + if [[ ! "$new_cmdline" =~ resume= ]]; then + new_cmdline="$new_cmdline resume=UUID=$SWAP_UUID" + log_info "Adding resume parameter" + changes_made=true + fi + + if [[ ! "$new_cmdline" =~ ipv6.disable= ]]; then + new_cmdline="$new_cmdline ipv6.disable=1" + log_info "Adding ipv6.disable parameter" + changes_made=true + fi + + new_cmdline=$(echo "$new_cmdline" | sed 's/ */ /g' | sed 's/^ *//' | sed 's/ *$//') + + if [[ "$changes_made" == "true" ]]; then + log_info "New kernel cmdline: $new_cmdline" + + local new_cmdline_escaped temp_file + new_cmdline_escaped=$(escape_for_sed "$new_cmdline") + temp_file=$(mktemp) + + sed "s|KERNEL_CMDLINE\[default\]=\".*\"|KERNEL_CMDLINE[default]=\"$new_cmdline_escaped\"|" "$LIMINE_CONFIG" >"$temp_file" + + if show_diff_and_confirm "$LIMINE_CONFIG" "LIMINE CONFIGURATION" "$temp_file"; then + cp "$temp_file" "$LIMINE_CONFIG" + log_info "Limine configuration updated successfully" + rm "$temp_file" + return 0 + else + log_info "Skipping Limine configuration update" + log_info "You can manually add: resume=UUID=$SWAP_UUID to your kernel cmdline" + rm "$temp_file" + return 1 + fi + else + log_info "Limine configuration already contains required parameters" + return 1 + fi +} + +update_mkinitcpio_config() { + log_info "Updating mkinitcpio configuration" + + backup_file "$MKINITCPIO_CONFIG" + + if grep -q '^HOOKS=.*resume' "$MKINITCPIO_CONFIG"; then + log_info "Resume hook already present in mkinitcpio.conf" + return 1 + fi + + log_info "Adding resume hook to mkinitcpio.conf" + + local current_hooks new_hooks temp_file + current_hooks=$(grep '^HOOKS=' "$MKINITCPIO_CONFIG") + log_info "Current HOOKS: $current_hooks" + + new_hooks=$(echo "$current_hooks" | sed 's/filesystems/filesystems resume/' | sed 's/resume resume/resume/') + + if [[ ! "$new_hooks" =~ resume ]]; then + new_hooks=$(echo "$current_hooks" | sed 's/)/ resume)/') + fi + + log_info "New HOOKS: $new_hooks" + + local new_hooks_escaped + new_hooks_escaped=$(escape_for_sed "$new_hooks") + temp_file=$(mktemp) + + sed "s|^HOOKS=.*|$new_hooks_escaped|" "$MKINITCPIO_CONFIG" >"$temp_file" + + if show_diff_and_confirm "$MKINITCPIO_CONFIG" "MKINITCPIO CONFIGURATION" "$temp_file"; then + cp "$temp_file" "$MKINITCPIO_CONFIG" + log_info "mkinitcpio.conf updated successfully" + rm "$temp_file" + return 0 + else + log_info "Skipping mkinitcpio.conf update" + log_info "You can manually add 'resume' to your HOOKS array" + rm "$temp_file" + return 1 + fi +} + +apply_system_changes() { + local limine_updated="$1" + local mkinitcpio_updated="$2" + local operations_needed=() + + log_info "System updates required" + + if [[ "$mkinitcpio_updated" == "true" ]]; then + operations_needed+=("Regenerate initramfs (mkinitcpio -P)") + fi + + if [[ "$limine_updated" == "true" ]]; then + operations_needed+=("Update Limine bootloader (limine-update)") + fi + + if [[ ${#operations_needed[@]} -eq 0 ]]; then + log_info "No system updates needed - configuration is already up to date" + return 0 + fi + + log_info "The following operations need to be performed:" + for i in "${!operations_needed[@]}"; do + log_info " $((i + 1)). ${operations_needed[$i]}" + done + + if ! confirm "Apply all pending system changes?" "y"; then + log_warn "Skipping system updates" + log_warn "Configuration files have been updated but system changes were not applied" + log_info "You will need to manually run:" + + if [[ "$mkinitcpio_updated" == "true" ]]; then + log_info " mkinitcpio -P" + fi + + if [[ "$limine_updated" == "true" ]]; then + log_info " limine-update" + fi + + log_warn "Hibernation will not work until these commands are executed" + return 1 + fi + + log_info "Applying system changes" + + if [[ "$mkinitcpio_updated" == "true" ]]; then + log_info "Regenerating initramfs" + if ! mkinitcpio -P; then + log_error "Failed to regenerate initramfs" + log_info "Please check the configuration and run 'mkinitcpio -P' manually" + exit 1 + fi + log_info "Initramfs regenerated successfully" + fi + + if [[ "$limine_updated" == "true" ]]; then + log_info "Updating Limine configuration" + if ! limine-update; then + log_error "Failed to update Limine bootloader" + log_info "Please run 'limine-update' manually" + exit 1 + fi + log_info "Limine bootloader updated successfully" + fi + + return 0 +} + +show_completion_summary() { + local limine_updated="$1" + local mkinitcpio_updated="$2" + + log_info "Hibernation configuration completed successfully" + echo + log_info "Configuration details:" + log_info " - Swap UUID: $SWAP_UUID" + + if [[ "$limine_updated" == "true" ]]; then + log_info " - Resume parameter added to kernel cmdline" + log_info " - IPv6 disabled (optional optimization)" + else + log_info " - Kernel cmdline already configured" + fi + + if [[ "$mkinitcpio_updated" == "true" ]]; then + log_info " - Resume hook added to initramfs" + else + log_info " - Resume hook already present in initramfs" + fi + + echo + log_info "Backups created:" + log_info " - $LIMINE_CONFIG.backup.*" + log_info " - $MKINITCPIO_CONFIG.backup.*" + echo + log_info "Next steps:" + log_info " 1. Reboot your system" + log_info " 2. Test hibernation with: systemctl hibernate" + log_info " 3. Wake up and verify everything works correctly" + echo + log_info "If you encounter issues, restore backups with:" + log_info " cp $LIMINE_CONFIG.backup.* $LIMINE_CONFIG" + log_info " cp $MKINITCPIO_CONFIG.backup.* $MKINITCPIO_CONFIG" + log_info " mkinitcpio -P && limine-update" +} + +main() { + init_script + + check_requirements + + local limine_updated mkinitcpio_updated + + if update_limine_config; then + limine_updated="true" + else + limine_updated="false" + fi + + if update_mkinitcpio_config; then + mkinitcpio_updated="true" + else + mkinitcpio_updated="false" + fi + + if apply_system_changes "$limine_updated" "$mkinitcpio_updated"; then + show_completion_summary "$limine_updated" "$mkinitcpio_updated" + finish_script 0 + else + finish_script 1 + fi +} + +main "$@" diff --git a/runs/nvim b/runs/nvim new file mode 100755 index 0000000..b74b9c4 --- /dev/null +++ b/runs/nvim @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# NAME: Setup Neovim configuration +# REQUIRES: interactive + +set -euo pipefail + +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} + +readonly NVIM_REPO="https://github.com/marianozunino/nvim.git" +readonly NVIM_CONFIG_DIR="$HOME/.config/nvim" + +check_requirements() { + if ! command_exists git; then + log_error "git is required but not installed" + exit 1 + fi +} + +setup_neovim_config() { + log_info "Setting up Neovim configuration" + + if [[ -d "$NVIM_CONFIG_DIR" ]]; then + local backup_dir="$NVIM_CONFIG_DIR.backup.$(date +%Y%m%d_%H%M%S)" + log_info "Backing up existing Neovim config to $backup_dir" + mv "$NVIM_CONFIG_DIR" "$backup_dir" + fi + + log_info "Cloning Neovim config to $NVIM_CONFIG_DIR" + git clone "$NVIM_REPO" "$NVIM_CONFIG_DIR" + + cd "$NVIM_CONFIG_DIR" + + if [[ -d ".githooks" ]]; then + git config core.hooksPath .githooks + chmod +x .githooks/* 2>/dev/null || true + log_info "Git hooks configured" + fi +} + +main() { + init_script + + check_requirements + setup_neovim_config + + log_info "Neovim configuration: $NVIM_CONFIG_DIR" + + finish_script 0 +} + +main "$@" diff --git a/runs/paru b/runs/paru index 3ad33ec..f9d9cea 100755 --- a/runs/paru +++ b/runs/paru @@ -1,26 +1,59 @@ #!/usr/bin/env bash -# NAME: Installs paru +# NAME: Install paru AUR helper + set -euo pipefail -if command -v paru &>/dev/null; then - echo "paru ya está instalado." - exit 0 -fi +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} -echo "Instalando dependencias necesarias..." -sudo pacman -S --needed --noconfirm base-devel git +check_requirements() { + local required_commands=(git makepkg) -tmpdir=$(mktemp -d) -echo "Temp dir: $tmpdir" -cd "$tmpdir" + for cmd in "${required_commands[@]}"; do + if ! command_exists "$cmd"; then + log_error "Required command not found: $cmd" + exit 1 + fi + done +} -echo "Cloning paru..." -git clone https://aur.archlinux.org/paru.git -cd paru +install_paru() { + if command_exists paru; then + log_info "paru is already installed" + return 0 + fi -echo "Compiling paru..." -makepkg -si --noconfirm + log_info "Installing build dependencies" + sudo pacman -S --needed --noconfirm base-devel git -echo "Cleaning up..." -cd .. -rm -rf "$tmpdir" + local tmpdir + tmpdir=$(mktemp -d) + log_info "Using temporary directory: $tmpdir" + + cd "$tmpdir" + + log_info "Cloning paru repository" + git clone https://aur.archlinux.org/paru.git + cd paru + + log_info "Building and installing paru" + makepkg -si --noconfirm + + log_info "Cleaning up temporary files" + cd / + rm -rf "$tmpdir" +} + +main() { + init_script + + check_requirements + install_paru + + finish_script 0 +} + +main "$@" diff --git a/runs/services b/runs/services index 8f3b02f..ecf0b18 100755 --- a/runs/services +++ b/runs/services @@ -3,103 +3,99 @@ set -euo pipefail -echo "Enabling and configuring system services..." +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} -# ═══════════════════════════════════════════════════════════ -# 🌐 NETWORKING SERVICES -# ═══════════════════════════════════════════════════════════ -echo "Enabling networking services..." +enable_networking_services() { + log_info "Enabling networking services" -# NetworkManager (main network management) -sudo systemctl enable --now NetworkManager.service + sudo systemctl enable --now NetworkManager.service + sudo systemctl enable --now bluetooth.service + sudo systemctl enable --now sshd.service +} -# Bluetooth -sudo systemctl enable --now bluetooth.service +enable_audio_services() { + log_info "Enabling audio services" -# SSH daemon -sudo systemctl enable --now sshd.service + systemctl --user enable --now pipewire.service + systemctl --user enable --now pipewire-pulse.service + systemctl --user enable --now wireplumber.service +} -# ═══════════════════════════════════════════════════════════ -# 🔊 AUDIO SERVICES -# ═══════════════════════════════════════════════════════════ -echo "Enabling audio services..." +enable_printing_services() { + log_info "Enabling printing services" -# PipeWire audio (replaces PulseAudio) -systemctl --user enable --now pipewire.service -systemctl --user enable --now pipewire-pulse.service -systemctl --user enable --now wireplumber.service + sudo systemctl enable --now cups.service +} -# ═══════════════════════════════════════════════════════════ -# 🖨️ PRINTING SERVICES -# ═══════════════════════════════════════════════════════════ -echo "Enabling printing services..." +configure_time_locale() { + log_info "Configuring time and locale services" -# CUPS printing system -sudo systemctl enable --now cups.service + if ! grep -q "en_US.UTF-8 UTF-8" /etc/locale.gen; then + echo "en_US.UTF-8 UTF-8" | sudo tee -a /etc/locale.gen + sudo locale-gen + fi -# ═══════════════════════════════════════════════════════════ -# ⏰ TIME & SCHEDULING SERVICES -# ═══════════════════════════════════════════════════════════ -echo "Configuring time and scheduling services..." + echo "LANG=en_US.UTF-8" | sudo tee /etc/locale.conf + log_info "System locale set to en_US.UTF-8" -echo "en_US.UTF-8 UTF-8" | sudo tee -a /etc/locale.gen -sudo locale-gen + sudo timedatectl set-timezone America/Montevideo + sudo timedatectl set-ntp true + sudo systemctl enable --now systemd-timesyncd.service + sudo systemctl enable --now cronie.service +} -# Set system locale -echo "LANG=en_US.UTF-8" | sudo tee /etc/locale.conf -echo "→ System locale set to en_US.UTF-8" +enable_security_services() { + log_info "Enabling security services" -sudo timedatectl set-timezone America/Montevideo + sudo systemctl enable --now ufw.service + sudo ufw --force enable + sudo ufw default deny incoming + sudo ufw default allow outgoing +} -# Enable NTP time synchronization -sudo timedatectl set-ntp true -sudo systemctl enable --now systemd-timesyncd.service +enable_backup_services() { + log_info "Enabling backup services" -# Cron daemon for scheduled tasks -sudo systemctl enable --now cronie.service + if findmnt / -t btrfs &>/dev/null; then + sudo systemctl enable --now snapper-timeline.timer + sudo systemctl enable --now snapper-cleanup.timer + log_info "Snapper enabled (BTRFS detected)" + else + log_info "Skipping Snapper (no BTRFS detected)" + fi +} -# ═══════════════════════════════════════════════════════════ -# 🔒 SECURITY & FIREWALL SERVICES -# ═══════════════════════════════════════════════════════════ -echo "Enabling security services..." +enable_power_management() { + log_info "Enabling power management" -# UFW firewall -sudo systemctl enable --now ufw.service + sudo systemctl enable --now power-profiles-daemon.service +} -# Enable basic firewall rules -sudo ufw --force enable -sudo ufw default deny incoming -sudo ufw default allow outgoing +enable_system_optimization() { + log_info "Enabling system optimization" -# ═══════════════════════════════════════════════════════════ -# 💾 BACKUP & SYNC SERVICES -# ═══════════════════════════════════════════════════════════ -echo "Enabling backup services..." + sudo systemctl enable --now haveged.service + sudo systemctl enable --now plocate-updatedb.timer + sudo systemctl enable --now syncthing@forbi.service +} -# Snapper for BTRFS snapshots (if using BTRFS) -if findmnt / -t btrfs &>/dev/null; then - sudo systemctl enable --now snapper-timeline.timer - sudo systemctl enable --now snapper-cleanup.timer - echo "→ Snapper enabled (BTRFS detected)" -else - echo "→ Skipping Snapper (no BTRFS detected)" -fi +main() { + init_script -# ═══════════════════════════════════════════════════════════ -# ⚡ POWER MANAGEMENT -# ═══════════════════════════════════════════════════════════ -echo "Enabling power management..." + enable_networking_services + enable_audio_services + enable_printing_services + configure_time_locale + enable_security_services + enable_backup_services + enable_power_management + enable_system_optimization -# Power profiles daemon -sudo systemctl enable --now power-profiles-daemon.service + finish_script 0 +} -# ═══════════════════════════════════════════════════════════ -# 🔧 SYSTEM OPTIMIZATION -# ═══════════════════════════════════════════════════════════ -echo "Enabling system optimization..." - -# Haveged entropy daemon -sudo systemctl enable --now haveged.service - -# Locate database updates -sudo systemctl enable --now plocate-updatedb.timer +main "$@" diff --git a/runs/sudo b/runs/sudo new file mode 100755 index 0000000..0688ee3 --- /dev/null +++ b/runs/sudo @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# NAME: Configure passwordless sudo for wheel group +# REQUIRES: sudo + +set -euo pipefail + +# Source common functions +source "$(dirname "$0")/../common.sh" || { + echo "[ERROR] Could not source common.sh" >&2 + exit 1 +} + +check_requirements() { + if ! is_root; then + log_error "This script requires root privileges" + log_info "Run with: sudo $0" + exit 1 + fi +} + +configure_sudo() { + local sudoers_file="/etc/sudoers.d/wheel" + local config_line="%wheel ALL=(ALL:ALL) NOPASSWD: ALL" + + log_sudo "Configuring wheel group for passwordless sudo" + + echo "$config_line" >"$sudoers_file" + chmod 440 "$sudoers_file" + + log_info "Wheel group configured for passwordless sudo" + log_info "Users in wheel group can now use sudo without password" +} + +main() { + init_script + + check_requirements + configure_sudo + + finish_script 0 +} +main "$@"