dev: automated commit - 2025-06-08 23:28:55

This commit is contained in:
Mariano Z. 2025-06-08 23:28:56 -03:00
parent 33106ea4a6
commit 0ab9f62ace
Signed by: marianozunino
GPG key ID: 4C73BAD25156DACE
13 changed files with 1255 additions and 715 deletions

252
common.sh Normal file
View file

@ -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"
}

102
main.go
View file

@ -32,11 +32,12 @@ type Config struct {
} }
type Script struct { type Script struct {
Path string // Full filesystem path Path string // Full filesystem path
Name string // Just filename (e.g. "install.sh") Name string // Just filename (e.g. "install.sh")
RelPath string // Relative path from runs/ (e.g. "tools/install.sh") RelPath string // Relative path from runs/ (e.g. "tools/install.sh")
Desc string // Description from script comment Desc string // Description from script comment
RequiresSudo bool // Whether script needs elevated privileges RequiresSudo bool // Whether script needs elevated privileges
RequiresInteractive bool // Whether script needs interactive input
} }
var config Config var config Config
@ -213,15 +214,20 @@ func handlePush() error {
return nil 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) file, err := os.Open(scriptPath)
if err != nil { if err != nil {
return "No description", false return "No description", ScriptMetadata{}
} }
defer file.Close() defer file.Close()
var desc string var desc string
var requiresSudo bool scriptMetadata := ScriptMetadata{}
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
@ -231,13 +237,9 @@ func getScriptMetadata(scriptPath string) (string, bool) {
desc = strings.TrimSpace(strings.TrimPrefix(line, "# NAME:")) desc = strings.TrimSpace(strings.TrimPrefix(line, "# NAME:"))
} }
if strings.HasPrefix(line, "# REQUIRES: sudo") || if strings.HasPrefix(line, "# REQUIRES:") {
strings.HasPrefix(line, "# REQUIRES:sudo") || scriptMetadata.RequiresSudo = strings.Contains(line, "sudo")
strings.HasPrefix(line, "# ELEVATED: true") || scriptMetadata.RequiresInteractive = strings.Contains(line, "interactive")
strings.HasPrefix(line, "# ELEVATED:true") ||
strings.HasPrefix(line, "# SUDO: true") ||
strings.HasPrefix(line, "# SUDO:true") {
requiresSudo = true
} }
} }
@ -245,7 +247,7 @@ func getScriptMetadata(scriptPath string) (string, bool) {
desc = "No description" desc = "No description"
} }
return desc, requiresSudo return desc, scriptMetadata
} }
func findScripts(filters []string) ([]Script, error) { func findScripts(filters []string) ([]Script, error) {
@ -271,14 +273,15 @@ func findScripts(filters []string) ([]Script, error) {
scriptName := filepath.Base(path) scriptName := filepath.Base(path)
if len(filters) == 0 || matchesFilters(relPath, scriptName, filters) { if len(filters) == 0 || matchesFilters(relPath, scriptName, filters) {
desc, requiresSudo := getScriptMetadata(path) desc, metaData := getScriptMetadata(path)
scripts = append(scripts, Script{ scripts = append(scripts, Script{
Path: path, Path: path,
Name: scriptName, Name: scriptName,
RelPath: relPath, RelPath: relPath,
Desc: desc, Desc: desc,
RequiresSudo: requiresSudo, 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") errorLog("sudo command not found - cannot run elevated script")
return fmt.Errorf("sudo not available") 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...) cmd = exec.Command("sudo", fullArgs...)
sudoLog("Running with sudo: %s", strings.Join(append([]string{script.Path}, args...), " ")) sudoLog("Running with sudo: %s", strings.Join(append([]string{script.Path}, args...), " "))
} else { } else {
cmd = exec.Command(script.Path, args...) cmd = exec.Command(script.Path, args...)
} }
if config.Interactive { if config.Interactive || script.RequiresInteractive {
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
} }
@ -348,7 +352,7 @@ func executeScript(script Script, args []string, verbose bool) error {
} }
defer logFileHandle.Close() defer logFileHandle.Close()
showOutput := verbose || config.Interactive showOutput := verbose || config.Interactive || script.RequiresInteractive
if showOutput { if showOutput {
cmd.Stdout = io.MultiWriter(os.Stdout, logFileHandle) cmd.Stdout = io.MultiWriter(os.Stdout, logFileHandle)
cmd.Stderr = io.MultiWriter(os.Stderr, logFileHandle) cmd.Stderr = io.MultiWriter(os.Stderr, logFileHandle)
@ -371,6 +375,7 @@ func createNewScript(scriptName string) error {
if !strings.HasSuffix(scriptName, ".sh") { if !strings.HasSuffix(scriptName, ".sh") {
scriptName += ".sh" scriptName += ".sh"
scriptPath = filepath.Join(config.RunsDir, scriptName)
} }
if _, err := os.Stat(scriptPath); err == nil { if _, err := os.Stat(scriptPath); err == nil {
@ -378,15 +383,52 @@ func createNewScript(scriptName string) error {
} }
template := fmt.Sprintf(`#!/usr/bin/env bash template := fmt.Sprintf(`#!/usr/bin/env bash
# NAME: %s script # NAME: %s
# REQUIRES: sudo # REQUIRES: sudo interactive
set -euo pipefail 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" check_requirements() {
`, strings.TrimSuffix(scriptName, ".sh"), scriptName, strings.TrimSuffix(scriptName, ".sh")) # 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) err := os.WriteFile(scriptPath, []byte(template), 0o755)
if err != nil { if err != nil {

View file

@ -1,34 +1,62 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NAME: aur script # NAME: Install AUR packages
set -euo pipefail 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 \ check_requirements() {
aws-session-manager-plugin \ if ! command_exists paru; then
bottles \ log_error "paru not found. Install it first"
bruno-bin \ exit 1
davmail \ fi
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
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 "$@"

378
runs/base
View file

@ -1,199 +1,237 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NAME: Install base system packages # NAME: Install base system packages with hardware detection
set -euo pipefail set -euo pipefail
AUR_HELPER="paru" # Source common functions
source "$(dirname "$0")/../common.sh" || {
if ! command -v $AUR_HELPER &>/dev/null; then echo "[ERROR] Could not source common.sh" >&2
echo "[ERROR] $AUR_HELPER not found. Install it first."
exit 1 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() {
# 🔧 DETECT HARDWARE local cpu_vendor gpu_info microcode gpu_packages
# ═══════════════════════════════════════════════════════════
CPU_VENDOR=$(lscpu | grep "Vendor ID" | awk '{print $3}')
GPU_INFO=$(lspci | grep -i vga)
echo "Detected CPU: $CPU_VENDOR" cpu_vendor=$(lscpu | grep "Vendor ID" | awk '{print $3}')
echo "Detected GPU: $GPU_INFO" gpu_info=$(lspci | grep -i vga)
# Determine microcode package log_info "Detected CPU: $cpu_vendor"
if [[ "$CPU_VENDOR" == "GenuineIntel" ]]; then log_info "Detected GPU: $gpu_info"
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
# Determine GPU drivers # Determine microcode package
GPU_PACKAGES="" case "$cpu_vendor" in
if echo "$GPU_INFO" | grep -qi "amd\|radeon"; then "GenuineIntel")
GPU_PACKAGES="xf86-video-amdgpu vulkan-radeon lib32-vulkan-radeon opencl-mesa lib32-opencl-mesa" microcode="intel-ucode"
echo "→ Using AMD GPU drivers" log_info "Using Intel microcode"
elif echo "$GPU_INFO" | grep -qi "intel"; then ;;
GPU_PACKAGES="xf86-video-intel vulkan-intel lib32-vulkan-intel intel-media-driver" "AuthenticAMD")
echo "→ Using Intel GPU drivers" microcode="amd-ucode"
elif echo "$GPU_INFO" | grep -qi "nvidia"; then log_info "Using AMD microcode"
GPU_PACKAGES="nvidia nvidia-utils lib32-nvidia-utils nvidia-settings" ;;
echo "→ Using NVIDIA drivers (you may want to review this)" *)
else microcode=""
echo "→ Unknown GPU, using generic drivers" log_warn "Unknown CPU vendor, skipping microcode"
fi ;;
esac
# ═══════════════════════════════════════════════════════════ # Determine GPU drivers
# 🔧 SYSTEM BASE gpu_packages=""
# ═══════════════════════════════════════════════════════════ if echo "$gpu_info" | grep -qi "amd\|radeon"; then
echo "Installing system base..." gpu_packages="xf86-video-amdgpu vulkan-radeon lib32-vulkan-radeon opencl-mesa lib32-opencl-mesa"
BASE_PACKAGES="base base-devel sudo stow linux-cachyos linux-cachyos-headers linux-firmware efibootmgr efitools mkinitcpio grub systemd-boot-manager" 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 install_base_system() {
BASE_PACKAGES="$BASE_PACKAGES $MICROCODE" log_info "Installing system base packages"
fi
$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"
# ═══════════════════════════════════════════════════════════ if [[ -n "$microcode" ]]; then
# 🌐 NETWORKING & BLUETOOTH base_packages="$base_packages $microcode"
# ═══════════════════════════════════════════════════════════ fi
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
# ═══════════════════════════════════════════════════════════ paru -S --needed --overwrite="*" $base_packages
# 🔊 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
# ═══════════════════════════════════════════════════════════ install_networking() {
# 🎨 FONTS & THEMES log_info "Installing networking packages"
# ═══════════════════════════════════════════════════════════
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
# ═══════════════════════════════════════════════════════════ paru -S --needed \
# 💻 DEVELOPMENT TOOLS networkmanager networkmanager-openvpn network-manager-applet \
# ═══════════════════════════════════════════════════════════ dhclient dnsmasq iptables-nft iwd wpa_supplicant wireless-regdb \
echo "Installing development tools..." bluez-libs blueman openssh
$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
# ═══════════════════════════════════════════════════════════ install_audio_video() {
# 🛠️ CLI UTILITIES log_info "Installing audio and video packages"
# ═══════════════════════════════════════════════════════════
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
# ═══════════════════════════════════════════════════════════ paru -S --needed \
# 🪟 WINDOW MANAGERS & DESKTOP pipewire pipewire-alsa pipewire-pulse wireplumber \
# ═══════════════════════════════════════════════════════════ pavucontrol alsa-firmware alsa-plugins alsa-utils \
echo "Installing window managers..." gst-libav gst-plugin-pipewire gst-plugins-bad gst-plugins-ugly \
$AUR_HELPER -S --noconfirm --needed \ mpv ffmpegthumbnailer
swaybg swayfx-git swayidle swaylock-effects waybar \ }
wlogout wdisplays wl-clipboard rofi-lbonn-wayland-git
# ═══════════════════════════════════════════════════════════ install_fonts_themes() {
# 🌍 GUI APPLICATIONS log_info "Installing fonts and themes"
# ═══════════════════════════════════════════════════════════
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
# ═══════════════════════════════════════════════════════════ paru -S --needed \
# 📁 SYSTEM UTILITIES adobe-source-han-sans-cn-fonts adobe-source-han-sans-jp-fonts \
# ═══════════════════════════════════════════════════════════ adobe-source-han-sans-kr-fonts adobe-source-serif-fonts \
echo "Installing system utilities..." awesome-terminal-fonts inter-font noto-fonts noto-fonts-cjk noto-fonts-emoji \
$AUR_HELPER -S --noconfirm --needed \ ttf-bitstream-vera ttf-dejavu ttf-fantasque-nerd ttf-fira-code ttf-fira-mono \
brightnessctl cronie cups cups-pdf gamemode gvfs gvfs-afc gvfs-google \ ttf-fira-sans ttf-font-awesome-5 ttf-liberation ttf-linux-libertine \
gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb haveged hdparm less lvm2 \ ttf-meslo-nerd ttf-ms-win11-auto ttf-opensans otf-font-awesome-5 otf-libertinus \
man-db man-pages meld modemmanager mtools mupdf netctl nss-mdns \ papirus-icon-theme rose-pine-gtk-theme
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
# ═══════════════════════════════════════════════════════════ install_development() {
# 🎮 GAMING & GRAPHICS log_info "Installing development tools"
# ═══════════════════════════════════════════════════════════
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"
if [[ -n "$GPU_PACKAGES" ]]; then paru -S --needed \
$AUR_HELPER -S --noconfirm --needed $GAMING_BASE $GPU_PACKAGES git git-lfs git-crypt github-cli lazygit \
else go go-task python python-defusedxml python-packaging \
$AUR_HELPER -S --noconfirm --needed $GAMING_BASE python-protobuf python-pynvim python-pywlroots lua luarocks \
fi ccls cmake ninja neovim-nightly-bin
}
# ═══════════════════════════════════════════════════════════ install_cli_utilities() {
# 📱 MOBILE & HARDWARE log_info "Installing CLI utilities"
# ═══════════════════════════════════════════════════════════
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
# ═══════════════════════════════════════════════════════════ paru -S --needed \
# ☁️ CLOUD & DEVOPS bat btop duf dysk dust eza fd fzf glances glow httpie ncdu \
# ═══════════════════════════════════════════════════════════ plocate ripgrep tealdeer the_silver_searcher tmux wget \
echo "Installing cloud and DevOps tools..." xdg-user-dirs zoxide yazi yq zsh
$AUR_HELPER -S --noconfirm --needed \ }
aws-cli aws-session-manager-plugin insomnia-bin \
kubectl kubectx kubefwd-bin ngrok postgresql
# ═══════════════════════════════════════════════════════════ install_window_managers() {
# 📚 DOCUMENT & PRODUCTIVITY log_info "Installing window managers and desktop environment"
# ═══════════════════════════════════════════════════════════
echo "Installing document tools..."
$AUR_HELPER -S --noconfirm --needed \
pandoc-cli texinfo wkhtmltopdf-bin zathura zathura-pdf-mupdf \
swappy wf-recorder
# ═══════════════════════════════════════════════════════════ paru -S --needed \
# 🔐 SECURITY & ENCRYPTION swaybg swayfx-git swayidle swaylock-effects waybar \
# ═══════════════════════════════════════════════════════════ wlogout wdisplays wl-clipboard rofi-lbonn-wayland-git
echo "Installing security tools..." }
$AUR_HELPER -S --noconfirm --needed \
yubico-authenticator-bin yubikey-manager-qt
# ═══════════════════════════════════════════════════════════ install_gui_applications() {
# 📦 PACKAGE MANAGERS & MISC log_info "Installing GUI applications"
# ═══════════════════════════════════════════════════════════
echo "Installing package managers and misc..."
$AUR_HELPER -S --noconfirm --needed \
pyenv watchman-bin wimlib
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 "$@"

View file

@ -1,37 +1,40 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NAME: Configure systemd-boot manager settings # NAME: Configure systemd-boot manager settings
# REQUIRES: sudo
set -euo pipefail set -euo pipefail
SDBOOT_CONFIG="/etc/sdboot-manage.conf" # Source common functions
source "$(dirname "$0")/../common.sh" || {
echo "Configuring systemd-boot manager..." echo "[ERROR] Could not source common.sh" >&2
# Check if sdboot-manage is installed
if ! command -v sdboot-manage &>/dev/null; then
echo "⚠️ sdboot-manage not found. Install systemd-boot-manager first."
exit 1 exit 1
fi }
# Backup existing config if it exists readonly SDBOOT_CONFIG="/etc/sdboot-manage.conf"
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
# Create the configuration check_requirements() {
sudo tee "$SDBOOT_CONFIG" >/dev/null <<'EOF' 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 # Config file for sdboot-manage
# Kernel options to be appended to the "options" line # 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_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. # When DISABLE_FALLBACK is set to "yes", it will stop creating fallback entries for each kernel.
DISABLE_FALLBACK="no" 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 # Setting REMOVE_EXISTING to "yes" will remove all your existing systemd-boot entries before building new entries
REMOVE_EXISTING="yes" 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 # When REMOVE_OBSOLETE is set to "yes" entries for kernels no longer available on the system will be removed
REMOVE_OBSOLETE="yes" 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 EOF
log_info "systemd-boot configuration updated"
}
main() {
init_script
check_requirements
configure_systemd_boot
finish_script 0
}
main "$@"

View file

@ -1,24 +1,41 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NAME: Install and configure Docker on Arch Linux # NAME: Install and configure Docker
set -euo pipefail 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 install_docker() {
sudo pacman -S --needed --noconfirm docker docker-compose docker-buildx log_info "Installing Docker packages"
sudo pacman -S --needed --noconfirm docker docker-compose docker-buildx
}
# Add current user to docker group configure_docker() {
sudo usermod -aG docker "$USER" log_info "Adding current user to docker group"
sudo usermod -aG docker "$USER"
# Enable and start Docker service log_info "Enabling Docker services"
sudo systemctl enable docker.service sudo systemctl enable docker.service
sudo systemctl start docker.service sudo systemctl start docker.service
sudo systemctl enable containerd.service
sudo systemctl start containerd.service
}
# Enable containerd service (dependency) main() {
sudo systemctl enable containerd.service init_script
sudo systemctl start containerd.service
echo "✅ Docker installed successfully!" install_docker
echo "⚠️ You need to log out and back in (or reboot) for group changes to take effect" configure_docker
echo "🐳 Test with: docker run hello-world"
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 "$@"

View file

@ -1,151 +1,118 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NAME: Setup dotfiles and Neovim configuration # NAME: Setup dotfiles and configuration
# REQUIRES: interactive
set -euo pipefail set -euo pipefail
DOTFILES_REPO="https://git.mz.uy/marianozunino/dotfiles.git" # Source common functions
NVIM_REPO="https://github.com/marianozunino/nvim.git" source "$(dirname "$0")/../common.sh" || {
DOTFILES_DIR="$HOME/dotfiles" echo "[ERROR] Could not source common.sh" >&2
NVIM_CONFIG_DIR="$HOME/.config/nvim" exit 1
}
echo "🏠 Setting up Forbi's development environment..." readonly DOTFILES_REPO="https://git.mz.uy/marianozunino/dotfiles.git"
readonly DOTFILES_DIR="$HOME/dotfiles"
# ═══════════════════════════════════════════════════════════ check_requirements() {
# 📁 CLONE DOTFILES local required_commands=(git git-crypt stow)
# ═══════════════════════════════════════════════════════════
echo "📦 Cloning dotfiles repository..."
if [[ -d "$DOTFILES_DIR" ]]; then for cmd in "${required_commands[@]}"; do
echo "→ Dotfiles directory already exists, updating..." if ! command_exists "$cmd"; then
cd "$DOTFILES_DIR" log_error "Required command not found: $cmd"
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 <your-key-id> 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
exit 1 exit 1
fi 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 fi
else }
echo "→ No git-crypt encryption detected"
fi
# ═══════════════════════════════════════════════════════════ handle_git_crypt() {
# ⚙️ CLONE NEOVIM CONFIG log_info "Checking git-crypt status"
# ═══════════════════════════════════════════════════════════ cd "$DOTFILES_DIR"
echo "📝 Setting up Neovim configuration..."
# Backup existing Neovim config if it exists if git-crypt status | grep -q "unlocked"; then
if [[ -d "$NVIM_CONFIG_DIR" ]]; then log_info "Repository already unlocked"
BACKUP_DIR="$NVIM_CONFIG_DIR.backup.$(date +%Y%m%d_%H%M%S)" return 0
echo "→ Backing up existing Neovim config to $BACKUP_DIR" fi
mv "$NVIM_CONFIG_DIR" "$BACKUP_DIR"
fi
# Clone Neovim configuration log_info "Repository is encrypted, attempting to unlock"
echo "→ Cloning Neovim config to $NVIM_CONFIG_DIR"
git clone "$NVIM_REPO" "$NVIM_CONFIG_DIR"
# ═══════════════════════════════════════════════════════════ if gpg --card-status &>/dev/null; then
# 🔧 CONFIGURE GIT HOOKS log_info "YubiKey detected, fetching public key"
# ═══════════════════════════════════════════════════════════
echo "🎣 Setting up Git hooks for Neovim config..."
cd "$NVIM_CONFIG_DIR" local key_id
if [[ -d ".githooks" ]]; then key_id=$(gpg --card-status 2>/dev/null | grep "General key info" -A 1 | tail -1 | awk '{print $1}' | sed 's/.*\///')
git config core.hooksPath .githooks
echo "→ Git hooks configured"
else
echo "→ No .githooks directory found, skipping"
fi
# ═══════════════════════════════════════════════════════════ if [[ -n "$key_id" ]] && ! gpg --list-keys "$key_id" &>/dev/null; then
# 📂 CREATE NECESSARY DIRECTORIES gpg --card-edit --batch --command-fd 0 <<<"fetch" &>/dev/null ||
# ═══════════════════════════════════════════════════════════ gpg --keyserver hkps://keys.openpgp.org --recv-keys "$key_id" &>/dev/null || true
echo "📂 Creating necessary directories..." fi
else
log_error "YubiKey not detected"
exit 1
fi
# Create .config directory if it doesn't exist if git-crypt unlock 2>/dev/null; then
mkdir -p "$HOME/.config" 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 create_directories() {
mkdir -p "$HOME/Dev" log_info "Creating necessary directories"
mkdir -p "$HOME/.local/bin" mkdir -p "$HOME/.config" "$HOME/Dev" "$HOME/.local/bin"
}
echo stow_packages() {
echo "# Install everything" cd "$DOTFILES_DIR"
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"
# ═══════════════════════════════════════════════════════════ log_info "Available stow packages:"
# 📋 SUMMARY & NEXT STEPS for dir in */; do
# ═══════════════════════════════════════════════════════════ if [[ -d "$dir" ]]; then
echo log_info " ${dir%/}"
echo "📁 Locations:" fi
echo " • Dotfiles: $DOTFILES_DIR" done
echo " • Neovim config: $NVIM_CONFIG_DIR"
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 <package_name>"
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 "$@"

View file

@ -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 <<EOF
SPACE_NUMBER=2
ENABLE_VERIFICATION=yes
BACKUP_THRESHOLD=8
BOOT_ORDER="*, *fallback, Snapshots"
ENABLE_SORT=no
ENABLE_LIMINE_FALLBACK=no
FIND_BOOTLOADERS=yes
ROOT_SNAPSHOTS_PATH="/@snapshots"
EOF
print_status "Creating custom Limine configuration..."
cat >"$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."

279
runs/limine_hibernate Executable file
View file

@ -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 "$@"

55
runs/nvim Executable file
View file

@ -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 "$@"

View file

@ -1,26 +1,59 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NAME: Installs paru # NAME: Install paru AUR helper
set -euo pipefail set -euo pipefail
if command -v paru &>/dev/null; then # Source common functions
echo "paru ya está instalado." source "$(dirname "$0")/../common.sh" || {
exit 0 echo "[ERROR] Could not source common.sh" >&2
fi exit 1
}
echo "Instalando dependencias necesarias..." check_requirements() {
sudo pacman -S --needed --noconfirm base-devel git local required_commands=(git makepkg)
tmpdir=$(mktemp -d) for cmd in "${required_commands[@]}"; do
echo "Temp dir: $tmpdir" if ! command_exists "$cmd"; then
cd "$tmpdir" log_error "Required command not found: $cmd"
exit 1
fi
done
}
echo "Cloning paru..." install_paru() {
git clone https://aur.archlinux.org/paru.git if command_exists paru; then
cd paru log_info "paru is already installed"
return 0
fi
echo "Compiling paru..." log_info "Installing build dependencies"
makepkg -si --noconfirm sudo pacman -S --needed --noconfirm base-devel git
echo "Cleaning up..." local tmpdir
cd .. tmpdir=$(mktemp -d)
rm -rf "$tmpdir" 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 "$@"

View file

@ -3,103 +3,99 @@
set -euo pipefail 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
}
# ═══════════════════════════════════════════════════════════ enable_networking_services() {
# 🌐 NETWORKING SERVICES log_info "Enabling networking services"
# ═══════════════════════════════════════════════════════════
echo "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 enable_audio_services() {
sudo systemctl enable --now bluetooth.service log_info "Enabling audio services"
# SSH daemon systemctl --user enable --now pipewire.service
sudo systemctl enable --now sshd.service systemctl --user enable --now pipewire-pulse.service
systemctl --user enable --now wireplumber.service
}
# ═══════════════════════════════════════════════════════════ enable_printing_services() {
# 🔊 AUDIO SERVICES log_info "Enabling printing services"
# ═══════════════════════════════════════════════════════════
echo "Enabling audio services..."
# PipeWire audio (replaces PulseAudio) sudo systemctl enable --now cups.service
systemctl --user enable --now pipewire.service }
systemctl --user enable --now pipewire-pulse.service
systemctl --user enable --now wireplumber.service
# ═══════════════════════════════════════════════════════════ configure_time_locale() {
# 🖨️ PRINTING SERVICES log_info "Configuring time and locale services"
# ═══════════════════════════════════════════════════════════
echo "Enabling printing services..."
# CUPS printing system if ! grep -q "en_US.UTF-8 UTF-8" /etc/locale.gen; then
sudo systemctl enable --now cups.service echo "en_US.UTF-8 UTF-8" | sudo tee -a /etc/locale.gen
sudo locale-gen
fi
# ═══════════════════════════════════════════════════════════ echo "LANG=en_US.UTF-8" | sudo tee /etc/locale.conf
# ⏰ TIME & SCHEDULING SERVICES log_info "System locale set to en_US.UTF-8"
# ═══════════════════════════════════════════════════════════
echo "Configuring time and scheduling services..."
echo "en_US.UTF-8 UTF-8" | sudo tee -a /etc/locale.gen sudo timedatectl set-timezone America/Montevideo
sudo locale-gen sudo timedatectl set-ntp true
sudo systemctl enable --now systemd-timesyncd.service
sudo systemctl enable --now cronie.service
}
# Set system locale enable_security_services() {
echo "LANG=en_US.UTF-8" | sudo tee /etc/locale.conf log_info "Enabling security services"
echo "→ System locale set to en_US.UTF-8"
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 enable_backup_services() {
sudo timedatectl set-ntp true log_info "Enabling backup services"
sudo systemctl enable --now systemd-timesyncd.service
# Cron daemon for scheduled tasks if findmnt / -t btrfs &>/dev/null; then
sudo systemctl enable --now cronie.service 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
}
# ═══════════════════════════════════════════════════════════ enable_power_management() {
# 🔒 SECURITY & FIREWALL SERVICES log_info "Enabling power management"
# ═══════════════════════════════════════════════════════════
echo "Enabling security services..."
# UFW firewall sudo systemctl enable --now power-profiles-daemon.service
sudo systemctl enable --now ufw.service }
# Enable basic firewall rules enable_system_optimization() {
sudo ufw --force enable log_info "Enabling system optimization"
sudo ufw default deny incoming
sudo ufw default allow outgoing
# ═══════════════════════════════════════════════════════════ sudo systemctl enable --now haveged.service
# 💾 BACKUP & SYNC SERVICES sudo systemctl enable --now plocate-updatedb.timer
# ═══════════════════════════════════════════════════════════ sudo systemctl enable --now syncthing@forbi.service
echo "Enabling backup services..." }
# Snapper for BTRFS snapshots (if using BTRFS) main() {
if findmnt / -t btrfs &>/dev/null; then init_script
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
# ═══════════════════════════════════════════════════════════ enable_networking_services
# ⚡ POWER MANAGEMENT enable_audio_services
# ═══════════════════════════════════════════════════════════ enable_printing_services
echo "Enabling power management..." configure_time_locale
enable_security_services
enable_backup_services
enable_power_management
enable_system_optimization
# Power profiles daemon finish_script 0
sudo systemctl enable --now power-profiles-daemon.service }
# ═══════════════════════════════════════════════════════════ main "$@"
# 🔧 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

42
runs/sudo Executable file
View file

@ -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 "$@"