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 {
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 {

View file

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

378
runs/base
View file

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

View file

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

View file

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

View file

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

View file

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

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