#!/bin/bash VERSION="1.0.0" VERSION_NUM="2" LAST_UPDATE="22.03.2025" GITLAB_URL="https://gl.wssk.ru/skasatkin/docker-rootless-install" GREEN=$(tput setaf 2) RESET=$(tput sgr0) RED=$(tput setaf 1) CYAN=$(tput setaf 6) TIMESTAMP=$(date +"%Y%m%d-%H%M%S") LOG_DIR="./.docker-install" LOG_FILE="$LOG_DIR/$TIMESTAMP.log" mkdir -p "$LOG_DIR" find "$LOG_DIR" -type f -mtime +7 -exec rm {} \; function log() { echo -e "$1" >> "$LOG_FILE" } function progress() { echo -ne "\r${CYAN}$1${RESET}\033[K" } function done_msg() { echo -e "\r${GREEN}✔ $1${RESET}\033[K" log "✔ $1" } function error_msg() { echo -e "\r${RED}✖ $1${RESET}\033[K" log "✖ $1" } function run_with_live_tail() { local command="$1" local lines=4 local buffer=() local prev_output="" echo -e "${CYAN}$progress_message${RESET}" for ((i=0; i<$lines; i++)); do echo "" done move_cursor_up() { echo -en "\033[${lines}A" } eval "$command" 2>&1 | while IFS= read -r line; do log "$line" if [[ "$line" == "$prev_output" ]]; then continue fi prev_output="$line" buffer+=("$line") if [ "${#buffer[@]}" -gt "$lines" ]; then buffer=("${buffer[@]: -$lines}") fi move_cursor_up for ((i=0; i<$lines; i++)); do local output="${buffer[$i]}" echo -en "\033[2K" echo "$output" done done move_cursor_up for ((i=0; i<$lines; i++)); do echo -en "\033[2K" echo "" done } function self_update() { echo -e "${CYAN}Проверка обновлений...${RESET}" version_file=$(curl -s "$GITLAB_URL/-/raw/master/version") remote_version=$(echo "$version_file" | grep VERSION= | cut -d '=' -f2 | tr -d '"' | tr -d '\r') remote_version_num=$(echo "$version_file" | grep VERSION_NUM= | cut -d '=' -f2 | tr -d '"' | tr -d '\r') echo -e "${CYAN}Получена версия: ${remote_version} (${remote_version_num})${RESET}" if [[ $remote_version_num -gt $VERSION_NUM ]]; then echo -e "${GREEN}Доступна новая версия: ${remote_version} (${remote_version_num})${RESET}" echo -e "${CYAN}Скачиваю обновление...${RESET}" curl -s -o docker-install.sh "$GITLAB_URL/-/raw/master/docker-install.sh" && \ chmod +x docker-install.sh echo -e "${GREEN}✅ Обновление завершено! Перезапусти скрипт.${RESET}" f_exit else echo -e "${GREEN}Установлена последняя версия: $VERSION${RESET}" fi } function install_docker() { clear log "=== Установка Docker (rootless)... ===" echo -e "${GREEN}Установка Docker (rootless)...${RESET}" if [ -f "$HOME/bin/dockerd" ]; then error_msg "⚠️ Rootless Docker уже установлен в $HOME/bin/dockerd" echo "" echo "👉 Для переустановки останови и удали:" echo " systemctl --user stop docker" echo " rm -f ~/bin/dockerd" echo "" echo -e "${RED}❗ Возвращаюсь в меню через 5 секунд...${RESET}" sleep 5 return 1 fi progress_message="⏳ Устанавливаю Docker (rootless)..." run_with_live_tail "curl -fsSL https://get.docker.com/rootless | sh" clear if [ $? -eq 0 ]; then done_msg "Docker успешно установлен!" else error_msg "Ошибка при установке Docker!" sleep 5 return 1 fi progress "⏳ Добавляю переменные окружения..." grep -qxF 'export PATH=$HOME/bin:$PATH' ~/.bashrc || echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc grep -qxF 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' ~/.bashrc || echo 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> ~/.bashrc source ~/.bashrc done_msg "Переменные окружения добавлены." echo -e "${CYAN}ℹ️ Но они могут не примениться. Поэтому перезайди в сессию или выполни:${RESET}" echo -e "source ~/.bashrc" progress "⏳ Запускаю и активирую Docker (user systemd)..." systemctl --user start docker systemctl --user enable docker done_msg "Docker запущен и добавлен в автозагрузку." return 0 } function install_compose() { clear log "=== Установка Docker Compose... ===" echo -e "${GREEN}Установка Docker Compose...${RESET}" if [ -f "$HOME/.docker/cli-plugins/docker-compose" ]; then error_msg "⚠️ Docker Compose уже установлен в ~/.docker/cli-plugins/docker-compose" echo "" echo "👉 Для переустановки удали:" echo " rm -f ~/.docker/cli-plugins/docker-compose" echo "" echo -e "${RED}❗ Возвращаюсь в меню через 5 секунд...${RESET}" sleep 5 return 1 fi progress "⏳ Создаю папку для docker-compose..." mkdir -p ~/.docker/cli-plugins/ done_msg "Папка ~/.docker/cli-plugins готова." progress_message="⏳ Скачиваю Docker Compose..." run_with_live_tail "curl -SL \"https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64\" -o ~/.docker/cli-plugins/docker-compose" if [ $? -eq 0 ]; then done_msg "Docker Compose скачан!" else error_msg "Ошибка при скачивании Docker Compose!" sleep 5 return 1 fi progress "⏳ Делаю docker-compose исполняемым..." chmod +x ~/.docker/cli-plugins/docker-compose done_msg "Права на выполнение добавлены." progress "⏳ Добавляю docker-compose в PATH..." grep -qxF 'export PATH=$HOME/.docker/cli-plugins:$PATH' ~/.bashrc || echo 'export PATH=$HOME/.docker/cli-plugins:$PATH' >> ~/.bashrc source ~/.bashrc done_msg "Переменная окружения обновлена." return 0 } function remove_docker() { clear log "=== Удаление Rootless Docker ===" echo -e "${RED}⚠️ Это удалит Rootless Docker, Compose и ВСЕ данные пользователя, ВКЛЮЧАЯ КОНТЕЙНЕРЫ, ОБРАЗЫ И ХРАНИЛИЩА!${RESET}" whiptail --title "Удаление Rootless Docker" --yesno "Действительно хочешь удалить Rootless Docker, Compose и ВСЕ КОНТЕЙНЕРЫ, ОБРАЗЫ И ХРАНИЛИЩА?" 12 60 response=$? if [[ $response -eq 0 ]]; then progress "⏳ Удаляю контейнеры, тома и образы..." if command -v docker >/dev/null 2>&1; then docker container rm -f $(docker ps -aq) 2>/dev/null docker volume rm $(docker volume ls -q) 2>/dev/null docker image rm -f $(docker image ls -q) 2>/dev/null done_msg "Контейнеры, тома и образы удалены." else error_msg "Docker не найден. Пропускаю удаление контейнеров и томов." fi progress "⏳ Останавливаю Rootless Docker (user)..." systemctl --user stop docker 2>/dev/null systemctl --user disable docker 2>/dev/null done_msg "Rootless Docker остановлен." progress "⏳ Удаляю systemd user сервисы..." rm -f ~/.config/systemd/user/docker.service ~/.config/systemd/user/docker.socket done_msg "Юниты удалены." progress "⏳ Удаляю исполняемые файлы Docker..." rm -rf ~/bin/docker* 2>/dev/null done_msg "Файлы из ~/bin удалены." progress "⏳ Чищу Docker data каталоги..." rm -rf ~/.local/share/docker ~/.docker done_msg "Данные Docker, какие возможно, удалены." echo -e "Если что-то осталось, и важно удалить, напиши мне." echo -e "${GREEN}✔ Rootless Docker и все данные удалены!${RESET}" else error_msg "❌ Удаление отменено пользователем." sleep 3 return 1 fi } function f_exit() { log "=== Завершение скрипта ===" log "exit code: ${1:-0}" echo -e "> logfile: $LOG_FILE" echo "" echo -e "${CYAN}Версия: $VERSION${RESET}" echo -e "${CYAN}Последнее обновление: $LAST_UPDATE${RESET}" echo "" echo -e "${CYAN}ℹ️ Если менялись переменные окружения, они могут не примениться. Поэтому перезайди в сессию или выполни:${RESET}" echo -e "source ~/.bashrc" exit ${1:-0} } function menu() { while true; do CHOICE=$(whiptail --title "Docker & Docker Compose ${VERSION}" --menu "\n\ Stеpan Kasatkin\n\ Версия: ${VERSION} | Обновлен: ${LAST_UPDATE}\n\n\ Выбери действие (стрелки + Enter):" \ 22 70 10 \ "1" "Установить Docker (rootless)" \ "2" "Установить Docker Compose" \ "3" "Установить Docker + Compose" \ "4" "Удалить Docker и ВСЕ данные" \ "0" "Выход" 3>&1 1>&2 2>&3) RETVAL=$? if [[ $RETVAL -eq 1 || $RETVAL -eq 255 ]]; then error_msg "❌ Отменено пользователем." f_exit 1 fi case $CHOICE in 1) install_docker ;; 2) install_compose ;; 3) install_docker docker_status=$? install_compose compose_status=$? if [[ $docker_status -eq 0 && $compose_status -eq 0 ]]; then done_msg "✅ Установка Docker и Compose завершена." echo -e "${GREEN}👉 Проверка версий:${RESET}" echo "docker --version" echo "docker-compose version" f_exit fi ;; 4) remove_docker ;; 0) echo -e "${GREEN}Выход...${RESET}" f_exit ;; *) error_msg "❌ Неверный выбор!" ;; esac done } clear echo -e "${GREEN}Docker & Docker Compose, Stepan Kasatkin${RESET}" log "=== Запуск установщика Docker & Compose ===" self_update menu