feat(install): rewrite install.sh with interactive mode selection
- 3 install modes: Full, Minimal, Custom (pick categories) - Dry-run flag (-n) to preview packages without installing - Progress bar with percentage during package install - Better visual hierarchy with section headers and dividers - Package categories: Core, Shell, Hyprland, UI, Hardware, Network, Audio, Build - Failed package summary at end - Cleaner banner and completion output
This commit is contained in:
1 file changed
+284
-186
+284
-186
@@ -10,104 +10,156 @@
|
|||||||
# A hackable shell for Hyprland
|
# A hackable shell for Hyprland
|
||||||
# Installation Script for Arch Linux
|
# Installation Script for Arch Linux
|
||||||
#
|
#
|
||||||
# Repository: https://github.com/S4NKALP/Modus --branch macos
|
# Repository: https://github.com/S4NKALP/Modus
|
||||||
# License: GPLv3
|
# License: GPLv3
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
set -u
|
set -u
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
|
# ── CLI flags ──────────────────────────────────────────────────────
|
||||||
AUTO_YES=false
|
AUTO_YES=false
|
||||||
|
DRY_RUN=false
|
||||||
|
MINIMAL=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
-y|--yes) AUTO_YES=true ;;
|
-y|--yes) AUTO_YES=true ;;
|
||||||
|
-n|--dry-run) DRY_RUN=true ;;
|
||||||
|
-m|--minimal) MINIMAL=true ;;
|
||||||
|
-h|--help)
|
||||||
|
echo "Usage: install.sh [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -y, --yes Skip all prompts"
|
||||||
|
echo " -n, --dry-run Show what would be installed without installing"
|
||||||
|
echo " -m, --minimal Install only core packages"
|
||||||
|
echo " -h, --help Show this help"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
REPO_URL="https://github.com/S4NKALP/Modus.git"
|
REPO_URL="https://github.com/S4NKALP/Modus.git"
|
||||||
INSTALL_DIR="$HOME/.config/Modus"
|
INSTALL_DIR="$HOME/.config/Modus"
|
||||||
|
|
||||||
PACKAGES=(
|
# ── Package definitions ────────────────────────────────────────────
|
||||||
|
CORE_PACKAGES=(
|
||||||
uv
|
uv
|
||||||
fabric-cli-git
|
fabric-cli-git
|
||||||
|
uwsm
|
||||||
|
)
|
||||||
|
|
||||||
|
SHELL_PACKAGES=(
|
||||||
cliphist
|
cliphist
|
||||||
gnome-bluetooth-3.0
|
|
||||||
slurp
|
slurp
|
||||||
ffmpeg
|
grim
|
||||||
|
swappy
|
||||||
|
wl-clipboard
|
||||||
|
wtype
|
||||||
|
libnotify
|
||||||
|
playerctl
|
||||||
|
matugen-bin
|
||||||
|
)
|
||||||
|
|
||||||
|
HYPRLAND_PACKAGES=(
|
||||||
hypridle
|
hypridle
|
||||||
hyprsunset
|
hyprsunset
|
||||||
hyprpicker
|
hyprpicker
|
||||||
hyprshot
|
hyprshot
|
||||||
grim
|
|
||||||
libnotify
|
|
||||||
matugen-bin
|
|
||||||
playerctl
|
|
||||||
gtk-session-lock
|
gtk-session-lock
|
||||||
|
)
|
||||||
|
|
||||||
|
UI_PACKAGES=(
|
||||||
awww
|
awww
|
||||||
apple-fonts
|
apple-fonts
|
||||||
swappy
|
|
||||||
wl-clipboard
|
|
||||||
webp-pixbuf-loader
|
webp-pixbuf-loader
|
||||||
wf-recorder
|
cinnamon-desktop
|
||||||
|
libmediaart
|
||||||
|
)
|
||||||
|
|
||||||
|
HARDWARE_PACKAGES=(
|
||||||
acpi
|
acpi
|
||||||
brightnessctl
|
brightnessctl
|
||||||
power-profiles-daemon
|
power-profiles-daemon
|
||||||
uwsm
|
|
||||||
cinnamon-desktop
|
|
||||||
ddcutil
|
ddcutil
|
||||||
at-spi2-core
|
at-spi2-core
|
||||||
gcc
|
)
|
||||||
make
|
|
||||||
pkgconf
|
NETWORK_PACKAGES=(
|
||||||
appmenu-gtk-module
|
|
||||||
libdbusmenu-gtk3
|
|
||||||
libdbusmenu-qt5
|
|
||||||
meson
|
|
||||||
ninja
|
|
||||||
wayland-protocols
|
|
||||||
libmediaart
|
|
||||||
networkmanager
|
networkmanager
|
||||||
network-manager-applet
|
network-manager-applet
|
||||||
blueman
|
blueman
|
||||||
libqalculate
|
|
||||||
wtype
|
|
||||||
pipewire
|
|
||||||
libpulse
|
|
||||||
pciutils
|
|
||||||
gtk-layer-shell
|
|
||||||
librsvg
|
|
||||||
gobject-introspection
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Colors and formatting
|
AUDIO_PACKAGES=(
|
||||||
|
pipewire
|
||||||
|
libpulse
|
||||||
|
)
|
||||||
|
|
||||||
|
BUILD_PACKAGES=(
|
||||||
|
gcc
|
||||||
|
make
|
||||||
|
pkgconf
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
wayland-protocols
|
||||||
|
gobject-introspection
|
||||||
|
gtk-layer-shell
|
||||||
|
librsvg
|
||||||
|
libqalculate
|
||||||
|
appmenu-gtk-module
|
||||||
|
libdbusmenu-gtk3
|
||||||
|
libdbusmenu-qt5
|
||||||
|
pciutils
|
||||||
|
wf-recorder
|
||||||
|
ffmpeg
|
||||||
|
)
|
||||||
|
|
||||||
|
ALL_PACKAGES=(
|
||||||
|
"${CORE_PACKAGES[@]}"
|
||||||
|
"${SHELL_PACKAGES[@]}"
|
||||||
|
"${HYPRLAND_PACKAGES[@]}"
|
||||||
|
"${UI_PACKAGES[@]}"
|
||||||
|
"${HARDWARE_PACKAGES[@]}"
|
||||||
|
"${NETWORK_PACKAGES[@]}"
|
||||||
|
"${AUDIO_PACKAGES[@]}"
|
||||||
|
"${BUILD_PACKAGES[@]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Colors ─────────────────────────────────────────────────────────
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ]; then
|
||||||
GREEN=$(tput setaf 2)
|
GREEN=$(tput setaf 2)
|
||||||
YELLOW=$(tput setaf 3)
|
YELLOW=$(tput setaf 3)
|
||||||
RED=$(tput setaf 1)
|
RED=$(tput setaf 1)
|
||||||
CYAN=$(tput setaf 6)
|
CYAN=$(tput setaf 6)
|
||||||
BLUE=$(tput setaf 4)
|
BLUE=$(tput setaf 4)
|
||||||
|
MAGENTA=$(tput setaf 5)
|
||||||
BOLD=$(tput bold)
|
BOLD=$(tput bold)
|
||||||
DIM=$(tput dim)
|
DIM=$(tput dim)
|
||||||
RESET=$(tput sgr0)
|
RESET=$(tput sgr0)
|
||||||
else
|
else
|
||||||
GREEN="" YELLOW="" RED="" CYAN="" BLUE="" BOLD="" DIM="" RESET=""
|
GREEN="" YELLOW="" RED="" CYAN="" BLUE="" MAGENTA="" BOLD="" DIM="" RESET=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Status symbols
|
|
||||||
ARROW="→"
|
ARROW="→"
|
||||||
CHECK="✔"
|
CHECK="✔"
|
||||||
CROSS="✖"
|
CROSS="✖"
|
||||||
INFO="ℹ"
|
INFO="ℹ"
|
||||||
WARN="⚠"
|
WARN="⚠"
|
||||||
|
BULLET="•"
|
||||||
|
|
||||||
# Progress tracking
|
# ── Helpers ────────────────────────────────────────────────────────
|
||||||
TOTAL_STEPS=12
|
header() {
|
||||||
CURRENT_STEP=0
|
echo ""
|
||||||
|
echo -e " ${BOLD}${CYAN}╔══════════════════════════════════════════════╗${RESET}"
|
||||||
|
echo -e " ${BOLD}${CYAN}║${RESET} ${BOLD}Modus Installer${RESET} ${DIM}v2.0${RESET} ${BOLD}${CYAN}║${RESET}"
|
||||||
|
echo -e " ${BOLD}${CYAN}╚══════════════════════════════════════════════╝${RESET}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
# Function for progress indicator
|
section() {
|
||||||
progress() {
|
echo ""
|
||||||
CURRENT_STEP=$((CURRENT_STEP + 1))
|
echo -e " ${BOLD}${MAGENTA}── $1 ──${RESET}"
|
||||||
echo -e "\n${BOLD}${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${RESET} ${BOLD}$1${RESET}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
step() {
|
step() {
|
||||||
@@ -123,13 +175,33 @@ warn() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
error() {
|
error() {
|
||||||
echo -e "\n${RED}${CROSS}${RESET} ${RED}${BOLD}ERROR:${RESET} ${RED}$1${RESET}\n" >&2
|
echo -e " ${RED}${CROSS}${RESET} ${RED}$1${RESET}"
|
||||||
}
|
}
|
||||||
|
|
||||||
info() {
|
info() {
|
||||||
echo -e " ${BLUE}${INFO}${RESET} ${DIM}$1${RESET}"
|
echo -e " ${BLUE}${INFO}${RESET} ${DIM}$1${RESET}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
divider() {
|
||||||
|
echo -e " ${DIM}──────────────────────────────────────────────${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
local prompt="$1"
|
||||||
|
local default="${2:-n}"
|
||||||
|
local yn
|
||||||
|
if [ "$AUTO_YES" = true ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ "$default" = "y" ]; then
|
||||||
|
read -rp " ${BOLD}${prompt} [Y/n]:${RESET} " yn
|
||||||
|
[[ ! "$yn" =~ ^[Nn]$ ]]
|
||||||
|
else
|
||||||
|
read -rp " ${BOLD}${prompt} [y/N]:${RESET} " yn
|
||||||
|
[[ "$yn" =~ ^[Yy]$ ]]
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
spinner() {
|
spinner() {
|
||||||
local pid=$1
|
local pid=$1
|
||||||
local message=$2
|
local message=$2
|
||||||
@@ -141,34 +213,36 @@ spinner() {
|
|||||||
i=$(((i + 1) % 10))
|
i=$(((i + 1) % 10))
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
printf "\r"
|
printf "\r %b%s%b %s\n" "$GREEN" "${CHECK}" "$RESET" "$message"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Cleanup handler
|
print_packages() {
|
||||||
|
local -n pkgs=$1
|
||||||
|
local count=${#pkgs[@]}
|
||||||
|
echo -e " ${DIM}${BULLET}${RESET} ${BOLD}$2${RESET} ${DIM}(${count} packages)${RESET}"
|
||||||
|
for pkg in "${pkgs[@]}"; do
|
||||||
|
echo -e " ${DIM}${BULLET}${RESET} ${pkg}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Cleanup ────────────────────────────────────────────────────────
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [ -n "${SUDO_KEEPER_PID:-}" ]; then
|
if [ -n "${SUDO_KEEPER_PID:-}" ]; then
|
||||||
kill $SUDO_KEEPER_PID 2>/dev/null || true
|
kill "$SUDO_KEEPER_PID" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
# Header
|
# ── Banner ─────────────────────────────────────────────────────────
|
||||||
clear
|
clear
|
||||||
echo -e "${BOLD}${CYAN}"
|
header
|
||||||
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
|
echo -e " ${BOLD}A hackable shell for Hyprland${RESET}"
|
||||||
progress "Pre-flight checks"
|
echo -e " ${DIM}https://github.com/S4NKALP/Modus${RESET}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Pre-flight checks ─────────────────────────────────────────────
|
||||||
|
section "Pre-flight checks"
|
||||||
|
|
||||||
step "Checking operating system..."
|
step "Checking operating system..."
|
||||||
if ! grep -qi "arch" /etc/os-release; then
|
if ! grep -qi "arch" /etc/os-release; then
|
||||||
@@ -184,25 +258,68 @@ if [ "$(id -u)" -eq 0 ]; then
|
|||||||
fi
|
fi
|
||||||
success "Running as regular user"
|
success "Running as regular user"
|
||||||
|
|
||||||
step "Checking system requirements..."
|
step "Checking git..."
|
||||||
if ! command -v git &>/dev/null; then
|
if ! command -v git &>/dev/null; then
|
||||||
error "git is not installed. Please install it first: sudo pacman -S git"
|
error "git is not installed. Run: sudo pacman -S git"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
success "All requirements met"
|
success "git found"
|
||||||
|
|
||||||
# Sudo authentication
|
# ── Install mode selection ────────────────────────────────────────
|
||||||
progress "Requesting permissions"
|
section "Install mode"
|
||||||
|
|
||||||
info "Some packages require root privileges for installation"
|
if [ "$MINIMAL" = true ]; then
|
||||||
echo ""
|
PACKAGES=("${CORE_PACKAGES[@]}")
|
||||||
if ! sudo -v; then
|
info "Minimal mode: ${#PACKAGES[@]} core packages only"
|
||||||
|
elif [ "$AUTO_YES" = true ]; then
|
||||||
|
PACKAGES=("${ALL_PACKAGES[@]}")
|
||||||
|
info "Full install: ${#PACKAGES[@]} packages"
|
||||||
|
else
|
||||||
|
echo -e " ${BOLD}Choose install mode:${RESET}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${GREEN}${ARROW}${RESET} ${BOLD}1) Full${RESET} ${DIM}— everything (${#ALL_PACKAGES[@]} packages)${RESET}"
|
||||||
|
echo -e " ${YELLOW}${ARROW}${RESET} ${BOLD}2) Minimal${RESET} ${DIM}— core only (${#CORE_PACKAGES[@]} packages)${RESET}"
|
||||||
|
echo -e " ${CYAN}${ARROW}${RESET} ${BOLD}3) Custom${RESET} ${DIM}— pick categories${RESET}"
|
||||||
|
echo ""
|
||||||
|
read -rp " ${BOLD}Select [1-3]:${RESET} " mode_choice
|
||||||
|
|
||||||
|
case "$mode_choice" in
|
||||||
|
2)
|
||||||
|
PACKAGES=("${CORE_PACKAGES[@]}")
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
PACKAGES=("${CORE_PACKAGES[@]}")
|
||||||
|
echo ""
|
||||||
|
for cat in "SHELL:Shell tools" "HYPRLAND:Hyprland extras" "UI:Desktop widgets" "HARDWARE:Hardware control" "NETWORK:Bluetooth & network" "AUDIO:Audio" "BUILD:Build tools"; do
|
||||||
|
local_name="${cat%%:*}"
|
||||||
|
local_label="${cat#*:}"
|
||||||
|
local_ref="${local_name}_PACKAGES[@]"
|
||||||
|
local_count="${#${!local_ref}}"
|
||||||
|
if confirm "${local_label} (${local_count} packages)?" "y"; then
|
||||||
|
PACKAGES+=("${!local_ref}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
PACKAGES=("${ALL_PACKAGES[@]}")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
divider
|
||||||
|
echo -e " ${BOLD}Will install ${GREEN}${#PACKAGES[@]}${RESET}${BOLD} packages${RESET}"
|
||||||
|
divider
|
||||||
|
|
||||||
|
# ── Sudo ───────────────────────────────────────────────────────────
|
||||||
|
section "Permissions"
|
||||||
|
|
||||||
|
info "Some packages require root privileges"
|
||||||
|
if ! sudo -v 2>/dev/null; then
|
||||||
error "Sudo authentication failed"
|
error "Sudo authentication failed"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
success "Permissions granted"
|
success "Sudo authenticated"
|
||||||
|
|
||||||
# Keep sudo alive
|
|
||||||
while true; do
|
while true; do
|
||||||
sudo -n true
|
sudo -n true
|
||||||
sleep 60
|
sleep 60
|
||||||
@@ -210,65 +327,62 @@ while true; do
|
|||||||
done 2>/dev/null &
|
done 2>/dev/null &
|
||||||
SUDO_KEEPER_PID=$!
|
SUDO_KEEPER_PID=$!
|
||||||
|
|
||||||
# Show package list
|
# ── Show package list ─────────────────────────────────────────────
|
||||||
progress "Package information"
|
section "Packages"
|
||||||
|
|
||||||
info "Total packages to install: ${BOLD}${#PACKAGES[@]}${RESET}"
|
if [ "$DRY_RUN" = true ]; then
|
||||||
echo ""
|
|
||||||
if [ "$AUTO_YES" = false ]; then
|
|
||||||
read -rp " ${YELLOW}${INFO}${RESET} View full package list? (y/N): " view_packages
|
|
||||||
else
|
|
||||||
view_packages="n"
|
|
||||||
fi
|
|
||||||
if [[ "$view_packages" =~ ^[Yy]$ ]]; then
|
|
||||||
echo ""
|
echo ""
|
||||||
printf " ${DIM}• %s${RESET}\n" "${PACKAGES[@]}"
|
printf " ${DIM}%s${RESET}\n" "${PACKAGES[@]}"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
info "Dry run — no packages will be installed"
|
||||||
|
|
||||||
# Confirmation
|
|
||||||
if [ "$AUTO_YES" = false ]; then
|
|
||||||
read -rp " ${BOLD}Proceed with installation? (y/N):${RESET} " confirm
|
|
||||||
else
|
|
||||||
confirm="y"
|
|
||||||
fi
|
|
||||||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
||||||
warn "Installation cancelled by user"
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# AUR helper setup
|
if confirm "View full package list?"; then
|
||||||
progress "Setting up AUR helper"
|
echo ""
|
||||||
|
printf " ${DIM}${BULLET} %s${RESET}\n" "${PACKAGES[@]}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
aur_helper="yay"
|
if ! confirm "Proceed with installation?"; then
|
||||||
|
warn "Cancelled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── AUR helper ─────────────────────────────────────────────────────
|
||||||
|
section "AUR helper"
|
||||||
|
|
||||||
|
AUR=""
|
||||||
if command -v paru &>/dev/null; then
|
if command -v paru &>/dev/null; then
|
||||||
aur_helper="paru"
|
AUR="paru"
|
||||||
success "Found paru"
|
success "Using paru"
|
||||||
elif command -v yay &>/dev/null; then
|
elif command -v yay &>/dev/null; then
|
||||||
success "Found yay"
|
AUR="yay"
|
||||||
|
success "Using yay"
|
||||||
else
|
else
|
||||||
step "Installing yay-bin..."
|
step "Installing yay-bin..."
|
||||||
tmpdir=$(mktemp -d)
|
tmpdir=$(mktemp -d)
|
||||||
(
|
(
|
||||||
git clone --quiet --depth=1 https://aur.archlinux.org/yay-bin.git "$tmpdir/yay-bin" 2>/dev/null || true
|
git clone --quiet --depth=1 https://aur.archlinux.org/yay-bin.git "$tmpdir/yay-bin"
|
||||||
cd "$tmpdir/yay-bin"
|
cd "$tmpdir/yay-bin"
|
||||||
makepkg -si --noconfirm >/dev/null 2>&1
|
makepkg -si --noconfirm >/dev/null 2>&1
|
||||||
) &
|
) &
|
||||||
spinner $! "Building yay-bin..."
|
spinner $! "Building yay-bin"
|
||||||
wait $! || {
|
wait $! || {
|
||||||
error "Failed to install yay-bin"
|
error "Failed to install yay-bin"
|
||||||
rm -rf "$tmpdir"
|
rm -rf "$tmpdir"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
rm -rf "$tmpdir"
|
rm -rf "$tmpdir"
|
||||||
success "yay-bin installed successfully"
|
AUR="yay"
|
||||||
|
success "yay-bin installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Repository setup
|
# ── Repository ─────────────────────────────────────────────────────
|
||||||
progress "Setting up Modus repository"
|
section "Repository"
|
||||||
|
|
||||||
if [ -d "$INSTALL_DIR" ]; then
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
step "Updating existing repository..."
|
step "Pulling latest changes..."
|
||||||
git -C "$INSTALL_DIR" pull --quiet 2>/dev/null || true
|
git -C "$INSTALL_DIR" pull --quiet 2>/dev/null || true
|
||||||
success "Repository updated"
|
success "Repository updated"
|
||||||
else
|
else
|
||||||
@@ -276,70 +390,59 @@ else
|
|||||||
git clone --quiet "$REPO_URL" "$INSTALL_DIR" 2>/dev/null || true
|
git clone --quiet "$REPO_URL" "$INSTALL_DIR" 2>/dev/null || true
|
||||||
success "Repository cloned"
|
success "Repository cloned"
|
||||||
fi
|
fi
|
||||||
info "Location: ${INSTALL_DIR}"
|
info "${INSTALL_DIR}"
|
||||||
|
|
||||||
# Package installation
|
# ── Install packages ──────────────────────────────────────────────
|
||||||
progress "Installing packages"
|
section "Installing packages"
|
||||||
|
|
||||||
step "Syncing package databases..."
|
step "Syncing databases..."
|
||||||
$aur_helper -Syy --noconfirm >/dev/null 2>&1 || true
|
$AUR -Syy --noconfirm >/dev/null 2>&1 || true
|
||||||
success "Database synced"
|
success "Databases synced"
|
||||||
|
|
||||||
step "Installing required packages (this may take a while)..."
|
|
||||||
installed=0
|
installed=0
|
||||||
failed=0
|
failed=0
|
||||||
|
failed_pkgs=()
|
||||||
|
total=${#PACKAGES[@]}
|
||||||
|
|
||||||
for pkg in "${PACKAGES[@]}"; do
|
for pkg in "${PACKAGES[@]}"; do
|
||||||
if $aur_helper -S --needed --noconfirm "$pkg" >/dev/null 2>&1; then
|
if $AUR -S --needed --noconfirm "$pkg" >/dev/null 2>&1; then
|
||||||
installed=$((installed + 1))
|
installed=$((installed + 1))
|
||||||
else
|
else
|
||||||
failed=$((failed + 1))
|
failed=$((failed + 1))
|
||||||
warn "Failed to install: $pkg"
|
failed_pkgs+=("$pkg")
|
||||||
fi
|
fi
|
||||||
printf "\r %b%s%b Progress: %s/%s packages" "$CYAN" "$ARROW" "$RESET" "$installed" "${#PACKAGES[@]}"
|
pct=$((installed * 100 / total))
|
||||||
|
printf "\r ${CYAN}${ARROW}${RESET} [%-50s] %3d%% (%d/%d)" "$(printf '#%.0s' $(seq 1 $((pct / 2))))" "$pct" "$installed" "$total"
|
||||||
done
|
done
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [ $failed -eq 0 ]; then
|
if [ $failed -eq 0 ]; then
|
||||||
success "All packages installed successfully"
|
success "All ${installed} packages installed"
|
||||||
else
|
else
|
||||||
warn "$failed package(s) failed to install"
|
warn "${failed} package(s) failed:"
|
||||||
|
for pkg in "${failed_pkgs[@]}"; do
|
||||||
|
echo -e " ${RED}${CROSS}${RESET} ${pkg}"
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update check
|
# ── Build native modules ──────────────────────────────────────────
|
||||||
step "Checking for package updates..."
|
section "Building native modules"
|
||||||
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
|
|
||||||
to_update+=("$pkg")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ ${#to_update[@]} -gt 0 ]; then
|
|
||||||
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"
|
|
||||||
fi
|
|
||||||
|
|
||||||
progress "Building Global Menu shim"
|
|
||||||
|
|
||||||
|
# Global Menu shim
|
||||||
step "Compiling libmenu_button_shim.so..."
|
step "Compiling libmenu_button_shim.so..."
|
||||||
SHIM_SRC="$INSTALL_DIR/src/window/globalmenu/libmenu_button_shim.c"
|
SHIM_SRC="$INSTALL_DIR/src/window/globalmenu/libmenu_button_shim.c"
|
||||||
SHIM_OUT="$INSTALL_DIR/src/window/globalmenu/libmenu_button_shim.so"
|
SHIM_OUT="$INSTALL_DIR/src/window/globalmenu/libmenu_button_shim.so"
|
||||||
if [ -f "$SHIM_SRC" ]; then
|
if [ -f "$SHIM_SRC" ]; then
|
||||||
if gcc -shared -fPIC -O2 -o "$SHIM_OUT" "$SHIM_SRC" "$(pkg-config --cflags --libs gtk+-3.0)" -ldl 2>/dev/null; then
|
if gcc -shared -fPIC -O2 -o "$SHIM_OUT" "$SHIM_SRC" "$(pkg-config --cflags --libs gtk+-3.0)" -ldl 2>/dev/null; then
|
||||||
success "libmenu_button_shim.so built successfully"
|
success "libmenu_button_shim.so built"
|
||||||
else
|
else
|
||||||
warn "Failed to compile libmenu_button_shim.so - global menu may not work"
|
warn "Failed to compile shim — global menu may not work"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
warn "libmenu_button_shim.c not found - skipping shim build"
|
warn "Shim source not found — skipped"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
progress "Building App Capture module"
|
# App Capture
|
||||||
|
|
||||||
step "Compiling libappcapture.so..."
|
step "Compiling libappcapture.so..."
|
||||||
if [ -d "$INSTALL_DIR/src/window/switcher/app-capture" ]; then
|
if [ -d "$INSTALL_DIR/src/window/switcher/app-capture" ]; then
|
||||||
if (
|
if (
|
||||||
@@ -347,38 +450,35 @@ if [ -d "$INSTALL_DIR/src/window/switcher/app-capture" ]; then
|
|||||||
meson setup builddir --wipe >/dev/null 2>&1 || meson setup builddir >/dev/null 2>&1
|
meson setup builddir --wipe >/dev/null 2>&1 || meson setup builddir >/dev/null 2>&1
|
||||||
meson compile -C builddir >/dev/null 2>&1
|
meson compile -C builddir >/dev/null 2>&1
|
||||||
); then
|
); then
|
||||||
success "libappcapture.so built successfully"
|
success "libappcapture.so built"
|
||||||
else
|
else
|
||||||
warn "Failed to compile App Capture module"
|
warn "Failed to compile app-capture"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
warn "App Capture module directory not found"
|
warn "App-capture source not found — skipped"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
progress "Configuring Hyprland"
|
# ── Hyprland config ───────────────────────────────────────────────
|
||||||
|
section "Hyprland configuration"
|
||||||
|
|
||||||
HYPR_CONFIG="$HOME/.config/hypr/hyprland.lua"
|
HYPR_CONFIG="$HOME/.config/hypr/hyprland.lua"
|
||||||
MODUS_MODULE_LINE="dofile(\"$INSTALL_DIR/config/hypr/modus.lua\")"
|
MODUS_MODULE_LINE="dofile(\"$INSTALL_DIR/config/hypr/modus.lua\")"
|
||||||
|
|
||||||
if [ -f "$HYPR_CONFIG" ]; then
|
if [ -f "$HYPR_CONFIG" ]; then
|
||||||
step "Checking Hyprland Lua configuration..."
|
|
||||||
|
|
||||||
if grep -qF "$MODUS_MODULE_LINE" "$HYPR_CONFIG"; then
|
if grep -qF "$MODUS_MODULE_LINE" "$HYPR_CONFIG"; then
|
||||||
success "Modus module already loaded"
|
success "Modus module already in hyprland.lua"
|
||||||
else
|
else
|
||||||
step "Adding Modus module to Hyprland Lua config..."
|
step "Adding Modus module to hyprland.lua..."
|
||||||
|
|
||||||
{
|
{
|
||||||
echo ""
|
echo ""
|
||||||
echo "-- Modus configuration"
|
echo "-- Modus configuration"
|
||||||
echo "$MODUS_MODULE_LINE"
|
echo "$MODUS_MODULE_LINE"
|
||||||
} >> "$HYPR_CONFIG"
|
} >> "$HYPR_CONFIG"
|
||||||
|
success "Module added"
|
||||||
success "Modus configuration added"
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
warn "Hyprland Lua config not found at $HYPR_CONFIG"
|
warn "hyprland.lua not found at ${HYPR_CONFIG}"
|
||||||
info "You may need to manually add: $MODUS_MODULE_LINE"
|
info "Add manually: ${MODUS_MODULE_LINE}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
step "Symlinking hypridle.conf..."
|
step "Symlinking hypridle.conf..."
|
||||||
@@ -387,16 +487,14 @@ HYPRIDLE_CONF="$INSTALL_DIR/config/hypr/hypridle.conf"
|
|||||||
HYPRIDLE_TARGET="$HYPR_DIR/hypridle.conf"
|
HYPRIDLE_TARGET="$HYPR_DIR/hypridle.conf"
|
||||||
mkdir -p "$HYPR_DIR"
|
mkdir -p "$HYPR_DIR"
|
||||||
if [ -f "$HYPRIDLE_CONF" ]; then
|
if [ -f "$HYPRIDLE_CONF" ]; then
|
||||||
if [ -L "$HYPRIDLE_TARGET" ]; then
|
|
||||||
rm "$HYPRIDLE_TARGET"
|
|
||||||
fi
|
|
||||||
ln -sf "$HYPRIDLE_CONF" "$HYPRIDLE_TARGET"
|
ln -sf "$HYPRIDLE_CONF" "$HYPRIDLE_TARGET"
|
||||||
success "hypridle.conf symlinked"
|
success "hypridle.conf symlinked"
|
||||||
else
|
else
|
||||||
warn "hypridle.conf not found at $HYPRIDLE_CONF"
|
warn "hypridle.conf not found — skipped"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
progress "Configuring global environment"
|
# ── Environment ───────────────────────────────────────────────────
|
||||||
|
section "Global environment"
|
||||||
|
|
||||||
APP_CONF_DIR="$HOME/.config/environment.d"
|
APP_CONF_DIR="$HOME/.config/environment.d"
|
||||||
APP_CONF_FILE="$APP_CONF_DIR/appmenu.conf"
|
APP_CONF_FILE="$APP_CONF_DIR/appmenu.conf"
|
||||||
@@ -407,7 +505,7 @@ cat > "$APP_CONF_FILE" << 'EOF'
|
|||||||
GTK_MODULES=appmenu-gtk-module
|
GTK_MODULES=appmenu-gtk-module
|
||||||
UBUNTU_MENUPROXY=1
|
UBUNTU_MENUPROXY=1
|
||||||
EOF
|
EOF
|
||||||
success "Environment config written to $APP_CONF_FILE"
|
success "environment.d/appmenu.conf written"
|
||||||
|
|
||||||
PAM_FILE="$HOME/.pam_environment"
|
PAM_FILE="$HOME/.pam_environment"
|
||||||
step "Updating pam_environment..."
|
step "Updating pam_environment..."
|
||||||
@@ -417,9 +515,11 @@ for entry in "GTK_MODULES DEFAULT=appmenu-gtk-module" "UBUNTU_MENUPROXY DEFAULT=
|
|||||||
echo "$entry" >> "$PAM_FILE"
|
echo "$entry" >> "$PAM_FILE"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
success ".pam_environment updated"
|
success "pam_environment updated"
|
||||||
|
|
||||||
|
# ── Matugen ────────────────────────────────────────────────────────
|
||||||
|
section "Matugen"
|
||||||
|
|
||||||
progress "Configuring Matugen"
|
|
||||||
MATUGEN_CONFIG="$HOME/.config/matugen/config.toml"
|
MATUGEN_CONFIG="$HOME/.config/matugen/config.toml"
|
||||||
|
|
||||||
MODUS_BLOCK='[templates.modus]
|
MODUS_BLOCK='[templates.modus]
|
||||||
@@ -432,34 +532,30 @@ input_path = "~/.config/Modus/config/matugen/templates/hyprland-colors.lua"
|
|||||||
output_path = "~/.config/Modus/config/matugen/colors.lua"'
|
output_path = "~/.config/Modus/config/matugen/colors.lua"'
|
||||||
|
|
||||||
if [ -f "$MATUGEN_CONFIG" ]; then
|
if [ -f "$MATUGEN_CONFIG" ]; then
|
||||||
step "Checking matugen config..."
|
|
||||||
|
|
||||||
# ---- Modus1 ----
|
|
||||||
if grep -qF "[templates.modus]" "$MATUGEN_CONFIG"; then
|
if grep -qF "[templates.modus]" "$MATUGEN_CONFIG"; then
|
||||||
success "modus1 template already exists"
|
success "Modus template already configured"
|
||||||
else
|
else
|
||||||
step "Adding modus1 template..."
|
step "Adding Modus template..."
|
||||||
echo "" >> "$MATUGEN_CONFIG"
|
echo "" >> "$MATUGEN_CONFIG"
|
||||||
echo "$MODUS_BLOCK" >> "$MATUGEN_CONFIG"
|
echo "$MODUS_BLOCK" >> "$MATUGEN_CONFIG"
|
||||||
success "modus template added"
|
success "Modus template added"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---- Hyprland ----
|
|
||||||
if grep -qF "[templates.hyprland]" "$MATUGEN_CONFIG"; then
|
if grep -qF "[templates.hyprland]" "$MATUGEN_CONFIG"; then
|
||||||
success "hyprland template already exists"
|
success "Hyprland template already configured"
|
||||||
else
|
else
|
||||||
step "Adding hyprland template..."
|
step "Adding Hyprland template..."
|
||||||
echo "" >> "$MATUGEN_CONFIG"
|
echo "" >> "$MATUGEN_CONFIG"
|
||||||
echo "$HYPR_BLOCK" >> "$MATUGEN_CONFIG"
|
echo "$HYPR_BLOCK" >> "$MATUGEN_CONFIG"
|
||||||
success "hyprland template added"
|
success "Hyprland template added"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
warn "matugen config not found at $MATUGEN_CONFIG"
|
warn "matugen config not found at ${MATUGEN_CONFIG}"
|
||||||
info "Create it first or install matugen"
|
info "Install matugen first, then re-run this script"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
progress "Launching Modus"
|
# ── Launch ─────────────────────────────────────────────────────────
|
||||||
|
section "Launch"
|
||||||
|
|
||||||
step "Stopping existing instances..."
|
step "Stopping existing instances..."
|
||||||
if killall modus 2>/dev/null; then
|
if killall modus 2>/dev/null; then
|
||||||
@@ -474,24 +570,26 @@ if uwsm app -- uv run --project "$INSTALL_DIR" start >/dev/null 2>&1 & then
|
|||||||
disown
|
disown
|
||||||
sleep 2
|
sleep 2
|
||||||
if pgrep -x "modus" >/dev/null; then
|
if pgrep -x "modus" >/dev/null; then
|
||||||
success "Modus is now running"
|
success "Modus is running"
|
||||||
else
|
else
|
||||||
warn "Modus may not have started correctly"
|
warn "Modus may not have started — check logs"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
error "Failed to start Modus"
|
error "Failed to start Modus"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Completion
|
# ── Summary ────────────────────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}${BOLD}╔════════════════════════════════════════╗${RESET}"
|
echo -e " ${GREEN}${BOLD}╔══════════════════════════════════════════╗${RESET}"
|
||||||
echo -e "${GREEN}${BOLD}║ ║${RESET}"
|
echo -e " ${GREEN}${BOLD}║${RESET} ${GREEN}${BOLD}║${RESET}"
|
||||||
echo -e "${GREEN}${BOLD}║ Installation completed! ║${RESET}"
|
echo -e " ${GREEN}${BOLD}║${RESET} ${GREEN}${BOLD}Installation complete!${RESET} ${GREEN}${BOLD}║${RESET}"
|
||||||
echo -e "${GREEN}${BOLD}║ ║${RESET}"
|
echo -e " ${GREEN}${BOLD}║${RESET} ${GREEN}${BOLD}║${RESET}"
|
||||||
echo -e "${GREEN}${BOLD}╚════════════════════════════════════════╝${RESET}"
|
echo -e " ${GREEN}${BOLD}╚══════════════════════════════════════════╝${RESET}"
|
||||||
echo ""
|
echo ""
|
||||||
info "Modus is running in the background"
|
divider
|
||||||
info "Config location: ${INSTALL_DIR}"
|
info "Packages: ${GREEN}${installed}${RESET} installed${RED:+, ${failed} failed}"
|
||||||
info "Repository: ${REPO_URL}"
|
info "Location: ${INSTALL_DIR}"
|
||||||
|
info "Config: ${INSTALL_DIR}/config/"
|
||||||
|
divider
|
||||||
echo ""
|
echo ""
|
||||||
Reference in new issue
Block a user