From c80bc27ac5ab0618b14e61f7ecd73f6fe6d8c4b6 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Wed, 15 Oct 2025 13:05:49 +0545 Subject: [PATCH] feat(install): improve and streamline installation script with added features and error handling --- install.sh | 331 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 260 insertions(+), 71 deletions(-) diff --git a/install.sh b/install.sh index 4cc384b..dc66055 100755 --- a/install.sh +++ b/install.sh @@ -10,12 +10,12 @@ # A hackable shell for Hyprland # Installation Script for Arch Linux # -# Repository: https://github.com/S4NKALP/Modus +# Repository: https://github.com/S4NKALP/Modus --branch macos # License: GPLv3 -set -e # Exit immediately if a command exits with a non-zero status -set -u # Treat unset variables as an error -set -o pipefail # Prevent errors in a pipeline from being masked +set -e +set -u +set -o pipefail REPO_URL="https://github.com/S4NKALP/Modus.git" INSTALL_DIR="$HOME/.config/Modus" @@ -36,7 +36,6 @@ PACKAGES=( libnotify matugen-bin playerctl - python-fabric-git python-gobject python-pillow python-setproctitle @@ -49,7 +48,6 @@ PACKAGES=( python-watchdog python-pyotp pyzbar - python-pywayland python-psutil python-pydbus python-thefuzz @@ -68,97 +66,227 @@ PACKAGES=( cinnamon-desktop ) -# Colors -GREEN=$(tput setaf 2) -YELLOW=$(tput setaf 3) -RED=$(tput setaf 1) -CYAN=$(tput setaf 6) -RESET=$(tput sgr0) +# Colors and formatting +if [ -t 1 ]; then + GREEN=$(tput setaf 2) + YELLOW=$(tput setaf 3) + RED=$(tput setaf 1) + CYAN=$(tput setaf 6) + BLUE=$(tput setaf 4) + BOLD=$(tput bold) + DIM=$(tput dim) + RESET=$(tput sgr0) +else + GREEN="" YELLOW="" RED="" CYAN="" BLUE="" BOLD="" DIM="" RESET="" +fi + +# Status symbols +ARROW="→" +CHECK="✔" +CROSS="✖" +INFO="ℹ" +WARN="⚠" + +# Progress tracking +TOTAL_STEPS=7 +CURRENT_STEP=0 + +# Function for progress indicator +progress() { + CURRENT_STEP=$((CURRENT_STEP + 1)) + echo -e "\n${BOLD}${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${RESET} ${BOLD}$1${RESET}" +} -# Function for status messages step() { - echo -e "${CYAN}→${RESET} $1" + echo -e " ${CYAN}${ARROW}${RESET} $1" } + success() { - echo -e "${GREEN}✔${RESET} $1" + echo -e " ${GREEN}${CHECK}${RESET} ${GREEN}$1${RESET}" } + warn() { - echo -e "${YELLOW}!${RESET} $1" + echo -e " ${YELLOW}${WARN}${RESET} ${YELLOW}$1${RESET}" } + error() { - echo -e "${RED}✖${RESET} $1" + echo -e "\n${RED}${CROSS}${RESET} ${RED}${BOLD}ERROR:${RESET} ${RED}$1${RESET}\n" >&2 } -echo -e "${GREEN}==================================" -echo " Modus Installer for Arch Linux" -echo -e "==================================${RESET}" +info() { + echo -e " ${BLUE}${INFO}${RESET} ${DIM}$1${RESET}" +} -# OS check +spinner() { + local pid=$1 + local message=$2 + local spin=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + local i=0 + + while kill -0 $pid 2>/dev/null; do + printf "\r ${CYAN}${spin[i]}${RESET} $message" + i=$(((i + 1) % 10)) + sleep 0.1 + done + printf "\r" +} + +# Cleanup handler +cleanup() { + if [ -n "${SUDO_KEEPER_PID:-}" ]; then + kill $SUDO_KEEPER_PID 2>/dev/null || true + fi +} +trap cleanup EXIT INT TERM + +# Header +clear +echo -e "${BOLD}${CYAN}" +cat << "EOF" + ███╗ ███╗ ██████╗ ██████╗ ██╗ ██╗███████╗ + ████╗ ████║██╔═══██╗██╔══██╗██║ ██║██╔════╝ + ██╔████╔██║██║ ██║██║ ██║██║ ██║███████╗ + ██║╚██╔╝██║██║ ██║██║ ██║██║ ██║╚════██║ + ██║ ╚═╝ ██║╚██████╔╝██████╔╝╚██████╔╝███████║ + ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ +EOF +echo -e "${RESET}" +echo -e "${BOLD} A hackable shell for Hyprland${RESET}" +echo -e "${DIM} Installation Script v1.0${RESET}\n" + +# Pre-flight checks +progress "Pre-flight checks" + +step "Checking operating system..." if ! grep -qi "arch" /etc/os-release; then - error "This script is designed for Arch Linux or Arch-based distro." + error "This script requires Arch Linux or an Arch-based distribution" exit 1 fi +success "Arch Linux detected" -# Root check +step "Checking user permissions..." if [ "$(id -u)" -eq 0 ]; then - error "Please do not run as root." + error "Please run this script as a regular user, not as root" exit 1 fi +success "Running as regular user" -# Warn about sudo -echo -e "\n${YELLOW}Some packages require root privileges (e.g., glace-git).${RESET}" -echo "You will be prompted for your sudo password now so the installation runs smoothly." -sudo -v || { - error "Sudo authentication failed." +step "Checking system requirements..." +if ! command -v git &>/dev/null; then + error "git is not installed. Please install it first: sudo pacman -S git" exit 1 -} +fi +success "All requirements met" -# Keep sudo alive until script finishes +# Sudo authentication +progress "Requesting permissions" + +info "Some packages require root privileges for installation" +echo "" +if ! sudo -v; then + error "Sudo authentication failed" + exit 1 +fi +success "Permissions granted" + +# Keep sudo alive while true; do sudo -n true sleep 60 kill -0 "$$" || exit done 2>/dev/null & +SUDO_KEEPER_PID=$! -# Confirm installation -echo -e "\nThis will install Modus and the following packages:" -printf "%s\n" "${PACKAGES[@]}" -read -rp "$(echo -e "${YELLOW}""Proceed? (y/N): ""${RESET}")" confirm +# Show package list +progress "Package information" + +info "Total packages to install: ${BOLD}${#PACKAGES[@]}${RESET}" +echo "" +read -rp " ${YELLOW}${INFO}${RESET} View full package list? (y/N): " view_packages +if [[ "$view_packages" =~ ^[Yy]$ ]]; then + echo "" + printf " ${DIM}• %s${RESET}\n" "${PACKAGES[@]}" + echo "" +fi + +# Confirmation +read -rp " ${BOLD}Proceed with installation? (y/N):${RESET} " confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then - warn "Installation cancelled." + warn "Installation cancelled by user" exit 0 fi -aur_helper="yay" +# AUR helper setup +progress "Setting up AUR helper" -# Check if paru exists, otherwise use yay +aur_helper="yay" if command -v paru &>/dev/null; then aur_helper="paru" -elif ! command -v yay &>/dev/null; then - echo "Installing yay-bin..." - tmpdir=$(mktemp -d) - git clone --depth=1 https://aur.archlinux.org/yay-bin.git "$tmpdir/yay-bin" - (cd "$tmpdir/yay-bin" && makepkg -si --noconfirm) - rm -rf "$tmpdir" -fi - -# Clone or update repo -if [ -d "$INSTALL_DIR" ]; then - step "Updating Modus repository..." - git -C "$INSTALL_DIR" pull + success "Found paru" +elif command -v yay &>/dev/null; then + success "Found yay" else - step "Cloning Modus repository..." - git clone "$REPO_URL" "$INSTALL_DIR" + step "Installing yay-bin..." + tmpdir=$(mktemp -d) + ( + git clone --quiet --depth=1 https://aur.archlinux.org/yay-bin.git "$tmpdir/yay-bin" 2>&1 | grep -v "Cloning into" || true + cd "$tmpdir/yay-bin" + makepkg -si --noconfirm >/dev/null 2>&1 + ) & + spinner $! "Building yay-bin..." + wait $! || { + error "Failed to install yay-bin" + rm -rf "$tmpdir" + exit 1 + } + rm -rf "$tmpdir" + success "yay-bin installed successfully" fi -success "Repository ready." -# Install packages -step "Installing required packages..." -$aur_helper -Syy --needed --noconfirm "${PACKAGES[@]}" || warn "Some packages failed to install." +# Repository setup +progress "Setting up Modus repository" -# Update outdated packages -step "Checking for outdated packages..." -outdated=$($aur_helper -Qu | awk '{print $1}' || true) +if [ -d "$INSTALL_DIR" ]; then + step "Updating existing repository..." + git -C "$INSTALL_DIR" pull --quiet 2>&1 | grep -v "Already up to date" || true + success "Repository updated" +else + step "Cloning repository..." + git clone --quiet "$REPO_URL" "$INSTALL_DIR" 2>&1 | grep -v "Cloning into" || true + success "Repository cloned" +fi +info "Location: ${INSTALL_DIR}" + +# Package installation +progress "Installing packages" + +step "Syncing package databases..." +$aur_helper -Syy --noconfirm >/dev/null 2>&1 || true +success "Database synced" + +step "Installing required packages (this may take a while)..." +installed=0 +failed=0 +for pkg in "${PACKAGES[@]}"; do + if $aur_helper -S --needed --noconfirm "$pkg" >/dev/null 2>&1; then + installed=$((installed + 1)) + else + failed=$((failed + 1)) + warn "Failed to install: $pkg" + fi + printf "\r ${CYAN}${ARROW}${RESET} Progress: ${installed}/${#PACKAGES[@]} packages" +done +echo "" + +if [ $failed -eq 0 ]; then + success "All packages installed successfully" +else + warn "$failed package(s) failed to install" +fi + +# Update check +step "Checking for package updates..." +outdated=$($aur_helper -Qu 2>/dev/null | awk '{print $1}' || true) to_update=() for pkg in "${PACKAGES[@]}"; do if echo "$outdated" | grep -q "^$pkg\$"; then @@ -167,21 +295,82 @@ for pkg in "${PACKAGES[@]}"; do done if [ ${#to_update[@]} -gt 0 ]; then - step "Updating outdated packages..." - $aur_helper -S --noconfirm "${to_update[@]}" + step "Updating ${#to_update[@]} outdated package(s)..." + $aur_helper -S --noconfirm "${to_update[@]}" >/dev/null 2>&1 || true + success "Packages updated" else - success "All packages are up-to-date." + success "All packages are up-to-date" fi -# Run config -# step "Running configuration..." -# python "$INSTALL_DIR/config/config.py" +# Configuration +# progress "Running configuration" +# if [ -f "$INSTALL_DIR/config/config.py" ]; then +# step "Initializing Modus configuration..." +# if python "$INSTALL_DIR/config/config.py" 2>/dev/null; then +# success "Configuration completed" +# else +# warn "Configuration step failed or was skipped" +# fi +# else +# info "No configuration file found, skipping" +# fi + +# Hyprland configuration +progress "Configuring Hyprland" + +HYPR_CONFIG="$HOME/.config/hypr/hyprland.conf" +MODUS_CONF_LINE="source= ~/.config/Modus/config/hypr/modus.conf" + +if [ -f "$HYPR_CONFIG" ]; then + step "Checking Hyprland configuration..." + if grep -qF "$MODUS_CONF_LINE" "$HYPR_CONFIG"; then + success "Modus configuration already sourced" + else + step "Adding Modus configuration to Hyprland..." + echo "" >> "$HYPR_CONFIG" + echo "# Modus configuration" >> "$HYPR_CONFIG" + echo "$MODUS_CONF_LINE" >> "$HYPR_CONFIG" + success "Modus configuration added to Hyprland" + fi +else + warn "Hyprland config not found at $HYPR_CONFIG" + info "You may need to manually add: $MODUS_CONF_LINE" +fi + +# Launch Modus +progress "Launching Modus" + +step "Stopping existing instances..." +if killall modus 2>/dev/null; then + success "Stopped running instance" + sleep 1 +else + info "No existing instance found" +fi -# Start Modus step "Starting Modus..." -killall modus 2>/dev/null || true -uwsm app -- python "$INSTALL_DIR/main.py" >/dev/null 2>&1 & -disown -success "Modus started successfully." +if uwsm app -- python "$INSTALL_DIR/main.py" >/dev/null 2>&1 & then + disown + sleep 1 + if pgrep -f "python.*main.py" >/dev/null; then + success "Modus is now running" + else + warn "Modus may not have started correctly" + fi +else + error "Failed to start Modus" + exit 1 +fi -echo -e "\n${GREEN}Installation complete!${RESET}" +# Completion +echo "" +echo -e "${GREEN}${BOLD}╔════════════════════════════════════════╗${RESET}" +echo -e "${GREEN}${BOLD}║ ║${RESET}" +echo -e "${GREEN}${BOLD}║ Installation completed! 🎉 ║${RESET}" +echo -e "${GREEN}${BOLD}║ ║${RESET}" +echo -e "${GREEN}${BOLD}╚════════════════════════════════════════╝${RESET}" +echo "" +info "Modus is running in the background" +info "Config location: ${INSTALL_DIR}" +info "Repository: ${REPO_URL}" +echo ""