#!/bin/bash # --- НАСТРОЙКИ --- # Цвета для вывода GREEN="\033[1;32m" YELLOW="\033[1;33m" RED="\033[1;31m" NC="\033[0m" # No Color # Путь к этому скрипту и его папке SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) # Имена файлов с пакетами PACMAN_PKGS="pkglist.txt" AUR_PKGS="aurlist.txt" # --- НАСТРОЙКИ СИМЛИНКОВ (Имена папок в репозитории) --- CONFIG_DIR_NAME="config" HOME_DIR_NAME="home" SYSTEM_DIR_NAME="system" # --- ХЕЛПЕРЫ --- msg() { echo -e "${GREEN}[INFO]${NC} $1" } warn() { echo -e "${YELLOW}[WARN]${NC} $1" } err() { echo -e "${RED}[ERROR]${NC} $1" } # --- ФУНКЦИИ --- # 1. Установка YAY (AUR Helper) install_yay() { if ! command -v yay &>/dev/null; then msg "YAY не найден. Установка YAY..." if ! pacman -Q git &>/dev/null || ! pacman -Q base-devel &>/dev/null; then sudo pacman -S --needed git base-devel fi rm -rf /tmp/yay git clone https://aur.archlinux.org/yay.git /tmp/yay (cd /tmp/yay && makepkg -si --noconfirm) rm -rf /tmp/yay msg "YAY установлен." else msg "YAY уже установлен." fi } # 2. Установка пакетов (Pacman и YAY) install_packages() { msg "Сбор пакетов для установки..." # Основные пакеты local pacman_args=$(sed 's/#.*//' "$SCRIPT_DIR/$PACMAN_PKGS" | grep -vE '^\s*$' | xargs) local aur_args=$(sed 's/#.*//' "$SCRIPT_DIR/$AUR_PKGS" | grep -vE '^\s*$' | xargs) # --- Поиск и выбор дополнительных пакетов --- local optional_pacman_lists=($(find "$SCRIPT_DIR" -maxdepth 1 -name "pkglist-*.txt" ! -name "$PACMAN_PKGS")) local optional_aur_lists=($(find "$SCRIPT_DIR" -maxdepth 1 -name "aurlist-*.txt" ! -name "$AUR_PKGS")) if [ ${#optional_pacman_lists[@]} -gt 0 ] || [ ${#optional_aur_lists[@]} -gt 0 ]; then warn "Найдены дополнительные списки пакетов:" for list in "${optional_pacman_lists[@]}"; do echo " -> $(basename "$list") (Pacman)"; done for list in "${optional_aur_lists[@]}"; do echo " -> $(basename "$list") (AUR)"; done read -p "Хотите выбрать пакеты из этих списков для установки? [y/N]: " choice < /dev/tty if [[ "$choice" == "y" || "$choice" == "Y" ]]; then for list in "${optional_pacman_lists[@]}"; do read -p " -> Установить из '$(basename "$list")'? [y/N]: " list_choice < /dev/tty if [[ "$list_choice" == "y" || "$list_choice" == "Y" ]]; then pacman_args+=" $(sed 's/#.*//' "$list" | grep -vE '^\s*$' | xargs)" fi done for list in "${optional_aur_lists[@]}"; do read -p " -> Установить из '$(basename "$list")'? [y/N]: " list_choice < /dev/tty if [[ "$list_choice" == "y" || "$list_choice" == "Y" ]]; then aur_args+=" $(sed 's/#.*//' "$list" | grep -vE '^\s*$' | xargs)" fi done fi fi # --- Установка пакетов Pacman --- if [[ ! -z "$pacman_args" ]]; then msg "Установка пакетов из Pacman..." sudo pacman -S --needed $pacman_args else msg "Список Pacman для установки пуст." fi # --- Установка пакетов AUR --- if [[ ! -z "$aur_args" ]]; then msg "Проверка и установка YAY..." install_yay msg "Установка пакетов из AUR..." yay -S --needed $aur_args else msg "Список AUR для установки пуст." fi msg "Установка пакетов завершена." } # 3. (НОВАЯ) Рекурсивная функция линковки link_item() { local src="$1" local dest="$2" # Проверка источника if [ ! -e "$src" ]; then return fi # 1. Если цель существует и это директория (и не симлинк) # И источник тоже директория # -> РЕКУРСИЯ (MERGE) if [ -d "$dest" ] && [ ! -L "$dest" ] && [ -d "$src" ]; then # msg " [DIR] Вход в $dest" for child in "$src"/*; do [ -e "$child" ] || continue local child_name=$(basename "$child") link_item "$child" "$dest/$child_name" done return fi # 2. Если цель существует (файл или симлинк) if [ -e "$dest" ] || [ -L "$dest" ]; then # Если это уже правильный симлинк - пропускаем if [ -L "$dest" ]; then local current_target=$(readlink -f "$dest") local source_absolute=$(readlink -f "$src") if [ "$current_target" == "$source_absolute" ]; then # msg " [OK] $dest уже указывает на правильный файл" return fi fi # Если это файл или неправильный симлинк - бэкап warn " [BACKUP] $dest существует. Бэкап -> $dest.bak" mv "$dest" "$dest.bak" fi # 3. Создаем симлинк # Создаем родительскую папку, если её нет (например для ~/.config/foo/bar) mkdir -p "$(dirname "$dest")" ln -sfn "$src" "$dest" msg " [LINK] $dest -> $src" } # 4. Создание символических ссылок (Обновлено) link_dotfiles() { msg "Создание символических ссылок..." # ВАЖНО: Предварительно создаем критические системные папки. # Это заставит функцию link_item использовать режим MERGE (рекурсию) для них, # вместо создания симлинка на всю папку. msg " -> Подготовка структуры папок..." mkdir -p "$HOME/.config" mkdir -p "$HOME/.local/share" mkdir -p "$HOME/.local/state" mkdir -p "$HOME/.local/bin" # mkdir -p "$HOME/bin" # Если используется # --- 1. Линкуем файлы в $HOME (из папки 'home') --- msg " -> Обработка '$HOME_DIR_NAME'..." local source_dir="$SCRIPT_DIR/$HOME_DIR_NAME" if [ -d "$source_dir" ]; then for item_path in "$source_dir"/*; do [ -e "$item_path" ] || continue # Пропуск, если папка пуста local item_name=$(basename "$item_path") # Вызываем рекурсивную функцию link_item "$item_path" "$HOME/$item_name" done fi # --- 2. Линкуем файлы в $HOME/.config (из папки 'config') --- msg " -> Обработка '$CONFIG_DIR_NAME'..." source_dir="$SCRIPT_DIR/$CONFIG_DIR_NAME" if [ -d "$source_dir" ]; then for item_path in "$source_dir"/*; do [ -e "$item_path" ] || continue local item_name=$(basename "$item_path") link_item "$item_path" "$HOME/.config/$item_name" done fi msg "Симлинки обработаны." } # 5. Проверка текущей конфигурации check_config() { msg "Проверка установленных пакетов Pacman..." local missing_pacman=0 # Ищем pkglist.txt и pkglist-*.txt for f in $(find "$SCRIPT_DIR" -maxdepth 1 -name "pkglist*.txt"); do while IFS= read -r line || [[ -n "$line" ]]; do local pkg=$(echo "$line" | sed 's/#.*//' | xargs) if [[ -z "$pkg" ]]; then continue; fi if [[ "$pkg" == "xorg" ]]; then continue; fi if ! pacman -Q "$pkg" &>/dev/null; then warn " -> Не найден (Pacman, из $(basename "$f")): $pkg" missing_pacman=1 fi done < "$f" done if [ $missing_pacman -eq 0 ]; then msg "Все пакеты Pacman установлены."; fi msg "Проверка установленных пакетов AUR..." local missing_aur=0 for f in $(find "$SCRIPT_DIR" -maxdepth 1 -name "aurlist*.txt"); do while IFS= read -r line || [[ -n "$line" ]]; do local pkg=$(echo "$line" | sed 's/#.*//' | xargs) if [[ -z "$pkg" ]]; then continue; fi if ! pacman -Q "$pkg" &>/dev/null; then warn " -> Не найден (AUR, из $(basename "$f")): $pkg" missing_aur=1 fi done < "$f" done if [ $missing_aur -eq 0 ]; then msg "Все пакеты AUR установлены."; fi msg "Проверка симлинков (выборочная)..." # Просто проверим наличие ссылок для корневых элементов local source_dir="$SCRIPT_DIR/$HOME_DIR_NAME" if [ -d "$source_dir" ]; then for item_path in "$source_dir"/*; do [ -e "$item_path" ] || continue local item_name=$(basename "$item_path") # Если это папка .local, проверяем глубже if [ "$item_name" == ".local" ]; then if [ ! -d "$HOME/.local" ]; then warn " -> Папка $HOME/.local отсутствует!"; fi elif [ ! -e "$HOME/$item_name" ]; then warn " -> Элемент не найден: $HOME/$item_name" fi done fi msg "Проверка завершена." } # 6. Проверка драйверов check_drivers() { msg "Проверка оборудования и рекомендации по драйверам..." if ! command -v lspci &>/dev/null; then warn "Команда lspci не найдена. Установите пакет 'pciutils'." return fi local vga=$(lspci -k | grep -A 2 -E "(VGA|3D)") if echo "$vga" | grep -iq "NVIDIA"; then warn "Найдена карта NVIDIA. Рекомендуемые пакеты:" warn " -> nvidia (или nvidia-lts, nvidia-dkms)" warn " -> lib32-nvidia-utils" elif echo "$vga" | grep -iq "Intel"; then msg "Найдена карта Intel. Рекомендуемые пакеты:" msg " -> mesa, lib32-mesa, vulkan-intel" elif echo "$vga" | grep -iq "AMD"; then msg "Найдена карта AMD/ATI. Рекомендуемые пакеты:" msg " -> mesa, lib32-mesa, vulkan-radeon" fi if lspci -k | grep -iq "broadcom"; then warn "Найден чип Broadcom. Возможно нужен: broadcom-wl-dkms" fi } # 7. Применение системных конфигов apply_system_configs() { msg "--- РЕЖИМ: ПРИМЕНЕНИЕ СИСТЕМНЫХ КОНФИГОВ ---" local system_source_dir="$SCRIPT_DIR/$SYSTEM_DIR_NAME" if [ ! -d "$system_source_dir" ]; then err "Папка '$system_source_dir' не найдена." return 1 fi find "$system_source_dir" -type f | while read -r source_file; do local rel_path=${source_file#$system_source_dir/} local target_file="/$rel_path" msg "Конфиг: $rel_path" if [ ! -f "$target_file" ]; then warn " Файл '$target_file' не существует в системе." read -p " Скопировать? [y/N]: " choice < /dev/tty if [[ "$choice" =~ ^[yY]$ ]]; then sudo mkdir -p "$(dirname "$target_file")" sudo cp "$source_file" "$target_file" msg " -> СКОПИРОВАНО." fi continue fi if command -v diff &>/dev/null; then if ! diff -q "$target_file" "$source_file" &>/dev/null; then msg " Найдены различия:" diff -u "$target_file" "$source_file" | head -n 20 warn " Заменить системный файл?" read -p " [y/N/b] (Да / Нет / Бэкап): " choice < /dev/tty case "$choice" in y|Y) sudo cp "$source_file" "$target_file" msg " -> ЗАМЕНЕНО." ;; b|B) sudo mv "$target_file" "$target_file.bak" sudo cp "$source_file" "$target_file" msg " -> БЭКАП И ЗАМЕНА." ;; esac else msg " -> Идентичны." fi else warn " diff не установлен, сравнение невозможно." fi done } # 8. Создание папок пользователя create_user_dirs() { msg "Проверка стандартных папок пользователя..." # Если xdg-user-dirs установлен, лучше использовать его if command -v xdg-user-dirs-update &>/dev/null; then xdg-user-dirs-update msg " -> Выполнен xdg-user-dirs-update" else mkdir -p "$HOME/Documents" "$HOME/Downloads" "$HOME/Pictures" msg " -> Папки созданы (fallback)." fi } # --- ГЛАВНЫЙ БЛОК --- show_help() { echo "Dotfiles Manager" echo "Команды:" echo " install - Установить пакеты, создать ссылки." echo " check - Проверка статуса." echo " update - git pull + install." echo " drivers - Рекомендации по драйверам." echo " system - Системные конфиги (/etc)." } if [ "$EUID" -eq 0 ]; then err "Запускайте от пользователя, не от root!" exit 1 fi if [ $# -eq 0 ]; then show_help; exit 0; fi case "$1" in install) msg "--- УСТАНОВКА ---" install_packages link_dotfiles create_user_dirs msg "Готово." ;; check) check_config ;; update) msg "--- ОБНОВЛЕНИЕ ---" if git pull; then install_packages link_dotfiles create_user_dirs msg "Обновлено." else err "Ошибка git pull." fi ;; drivers) check_drivers ;; system) apply_system_configs ;; help|*) show_help ;; esac