From 0d96105a9563c2942ebbe4deea4c99d2cf6a1163 Mon Sep 17 00:00:00 2001
From: goodhumored <goodhumored@vk.com>
Date: Wed, 23 Apr 2025 15:43:06 +0300
Subject: [PATCH] feat: scripts for server initialization

---
 init.sh       | 135 +++++++++++++++++++++++++++++++++++++++++++++
 local_seed.sh |  91 ++++++++++++++++++++++++++++++
 seed.sh       | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++
 seed_v2.sh    | 114 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 490 insertions(+)
 create mode 100755 init.sh
 create mode 100755 local_seed.sh
 create mode 100755 seed.sh
 create mode 100644 seed_v2.sh

diff --git a/init.sh b/init.sh
new file mode 100755
index 0000000..4b376ce
--- /dev/null
+++ b/init.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m' # No Color
+
+# Function to print messages
+info() {
+    echo -e "${GREEN}[INFO] $1${NC}"
+}
+
+error() {
+    echo -e "${RED}[ERROR] $1${NC}" >&2
+    exit 1
+}
+
+# Detect OS
+OS=""
+if [ -f /etc/os-release ]; then
+    . /etc/os-release
+    case $ID in
+        ubuntu)
+            OS="ubuntu"
+            ;;
+        arch)
+            OS="arch"
+            ;;
+        *)
+            error "Unsupported OS: $ID"
+            ;;
+    esac
+else
+    error "Cannot detect OS. /etc/os-release not found."
+fi
+
+info "Detected OS: $OS"
+
+# Ensure we're in the dotfiles directory
+if [ ! -f "$(pwd)/.gitmodules" ]; then
+    error "This script must be run from the dotfiles repository directory"
+fi
+
+# Install prerequisites
+info "Installing prerequisites..."
+
+if [ "$OS" = "ubuntu" ]; then
+    # Update package lists
+    sudo apt update && sudo apt upgrade -y
+
+    # Install required packages
+    sudo apt install -y git tmux stow fzf gcc
+
+    # Install optional packages (bat, fd-find, zoxide)
+    sudo apt install -y bat fd-find
+    curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
+
+    # Install eza (community-maintained ls alternative)
+    sudo mkdir -p /etc/apt/keyrings
+    wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc | sudo gpg --dearmor -o /etc/apt/keyrings/gierens.gpg
+    echo "deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://deb.gierens.de stable main" | sudo tee /etc/apt/sources.list.d/gierens.list
+    sudo chmod 644 /etc/apt/keyrings/gierens.gpg /etc/apt/sources.list.d/gierens.list
+    sudo apt update
+    sudo apt install -y eza
+
+    # Install Neovim from GitHub releases (latest stable)
+    info "Installing Neovim from GitHub releases..."
+    NVIM_VERSION=$(curl -s https://api.github.com/repos/neovim/neovim/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+    wget -O /tmp/nvim-linux64.tar.gz "https://github.com/neovim/neovim/releases/download/${NVIM_VERSION}/nvim-linux64.tar.gz"
+    sudo tar -C /usr/local -xzf /tmp/nvim-linux64.tar.gz
+    sudo ln -sf /usr/local/nvim-linux64/bin/nvim /usr/local/bin/nvim
+    rm /tmp/nvim-linux64.tar.gz
+
+    # Install thefuck
+    sudo apt install -y python3-pip
+    pip3 install thefuck --user
+
+elif [ "$OS" = "arch" ]; then
+    # Ensure yay is installed for AUR packages
+    if ! command -v yay &> /dev/null; then
+        info "Installing yay..."
+        sudo pacman -S --needed git base-devel
+        git clone https://aur.archlinux.org/yay.git /tmp/yay
+        cd /tmp/yay
+        makepkg -si --noconfirm
+        cd - && rm -rf /tmp/yay
+    fi
+
+    # Install all packages
+    yay -S --noconfirm git tmux neovim zoxide stow fzf bat eza fd gcc thefuck
+fi
+
+# Initialize and update submodules
+info "Initializing git submodules..."
+git submodule update --init --recursive
+
+# Backup existing config files
+info "Backing up existing configuration files..."
+BACKUP_DIR="$HOME/.dotfiles_backup_$(date +%F_%H-%M-%S)"
+mkdir -p "$BACKUP_DIR"
+
+for file in .gitconfig .zshrc .tmux.conf .bashrc; do
+    [ -f "$HOME/$file" ] && mv "$HOME/$file" "$BACKUP_DIR/$file" && info "Backed up ~/$file to $BACKUP_DIR/$file"
+done
+
+for dir in .config/nvim .config/mc kam .oh-my-zsh .tmux; do
+    [ -d "$HOME/$dir" ] && mv "$HOME/$dir" "$BACKUP_DIR/$(basename $dir)" && info "Backed up ~/$dir to $BACKUP_DIR/$(basename $dir)"
+done
+
+# Create symlinks with stow
+info "Creating symlinks with stow..."
+stow .
+
+# Change shell to zsh
+if [ "$(basename "$SHELL")" != "zsh" ]; then
+    info "Changing shell to zsh..."
+    ZSH_PATH=$(which zsh)
+    if [ -z "$ZSH_PATH" ]; then
+        error "zsh not found. Please install zsh and run 'chsh -s $(which zsh)' manually."
+    fi
+    chsh -s "$ZSH_PATH"
+    info "Shell changed to zsh. Please log out and log back in for the change to take effect."
+fi
+
+# Final instructions
+info "Dotfiles setup complete!"
+echo "To source tmux configuration:"
+echo "1. Start tmux with 'tmux'"
+echo "2. Press <prefix>: (default is Ctrl+b)"
+echo "3. Type 'source ~/.tmux.conf' and press Enter"
+echo
+echo "Backup of previous configs is stored in: $BACKUP_DIR"
+echo "You may need to log out and log back in for zsh to take effect."
diff --git a/local_seed.sh b/local_seed.sh
new file mode 100755
index 0000000..596fd63
--- /dev/null
+++ b/local_seed.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m' # No Color
+
+# Default values
+ANONYMOUS=false
+USERNAME="goodhumored"
+
+# Function to print messages
+info() {
+    echo -e "${GREEN}[INFO] $1${NC}"
+}
+
+error() {
+    echo -e "${RED}[ERROR] $1${NC}" >&2
+    exit 1
+}
+
+# Function to generate random username for anonymous mode
+generate_random_username() {
+    echo "user$(head /dev/urandom | tr -dc a-z0-9 | head -c 8)"
+}
+
+# Function to display usage
+usage() {
+    echo "Usage: $0 [options]"
+    echo "Options:"
+    echo "  -u, --username    Username to create (default: goodhumored)"
+    echo "  -a, --anonymous   Run in anonymous mode (random username, no dotfiles)"
+    exit 1
+}
+
+# Parse command line arguments
+while [[ "$#" -gt 0 ]]; do
+    case $1 in
+        -u|--username) USERNAME="$2"; shift ;;
+        -a|--anonymous) ANONYMOUS=true ;;
+        *) echo "Unknown parameter: $1"; usage ;;
+    esac
+    shift
+done
+
+# Set username for anonymous mode
+if [ "$ANONYMOUS" = true ]; then
+    USERNAME=$(generate_random_username)
+fi
+
+# Check if running as root
+if [ "$(id -u)" -ne 0 ]; then
+    error "This script must be run with sudo privileges"
+fi
+
+# Create user and setup home directory
+info "Creating user $USERNAME..."
+useradd -m -s /bin/bash -G sudo "$USERNAME"
+chmod 700 "/home/$USERNAME"
+
+# Setup SSH directory and authorized_keys
+info "Setting up SSH directory..."
+mkdir -p "/home/$USERNAME/.ssh"
+chmod 700 "/home/$USERNAME/.ssh"
+touch "/home/$USERNAME/.ssh/authorized_keys"
+chmod 600 "/home/$USERNAME/.ssh/authorized_keys"
+chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/.ssh"
+
+# Install dependencies
+info "Installing dependencies..."
+apt-get update
+apt-get install -y git stow
+
+# Clone and setup dotfiles (non-anonymous mode only)
+if [ "$ANONYMOUS" = false ]; then
+    info "Cloning dotfiles repository..."
+    su - "$USERNAME" -c "git clone https://github.com/goodhumored/dotfiles /home/$USERNAME/dotfiles"
+    info "Running init.sh..."
+    su - "$USERNAME" -c "cd /home/$USERNAME/dotfiles && ./init.sh"
+fi
+
+info "Local server setup complete!"
+echo "Username: $USERNAME"
+if [ "$ANONYMOUS" = false ]; then
+    echo "Dotfiles have been cloned and initialized."
+else
+    echo "Anonymous mode: No dotfiles cloned."
+fi
+echo "You can now add an SSH public key to /home/$USERNAME/.ssh/authorized_keys"
diff --git a/seed.sh b/seed.sh
new file mode 100755
index 0000000..ec554e0
--- /dev/null
+++ b/seed.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+set -e
+
+# Defaults
+ANONYMOUS=false
+PORT=22
+NEW_USER="goodhumored"
+PASS_DIR="$HOME/.password-store"
+
+# Random username for anonymous mode
+generate_random_username() {
+    echo "user$(head /dev/urandom | tr -dc a-z0-9 | head -c 8)"
+}
+
+# Usage
+usage() {
+    echo "Usage: $0 [options]"
+    echo "  -h, --host        Server IP"
+    echo "  -p, --port        SSH port (default: 22)"
+    echo "  -r, --root_user   Sudo user for SSH"
+    echo "  -s, --server_name Server name for SSH config"
+    echo "  -u, --username    User to create (default: goodhumored)"
+    echo "  -n, --pass_name   Password store name"
+    echo "  -a, --anonymous   Anonymous mode"
+    exit 1
+}
+
+# Check sshpass
+if ! command -v sshpass &> /dev/null; then
+    echo "Ошибка: нужен sshpass."
+    echo "Установи: sudo apt-get install sshpass (Ubuntu) или sudo pacman -S sshpass (Arch)"
+    exit 1
+fi
+
+# Parse args
+while [[ "$#" -gt 0 ]]; do
+    case $1 in
+        -h|--host) HOST="$2"; shift ;;
+        -p|--port) PORT="$2"; shift ;;
+        -r|--root_user) ROOT_USER="$2"; shift ;;
+        -s|--server_name) SERVER_NAME="$2"; shift ;;
+        -u|--username) NEW_USER="$2"; shift ;;
+        -n|--pass_name) PASS_NAME="$2"; shift ;;
+        -a|--anonymous) ANONYMOUS=true ;;
+        *) echo "Неизвестный параметр: $1"; usage ;;
+    esac
+    shift
+done
+
+# Validate args
+if [ -z "$HOST" ] || [ -z "$ROOT_USER" ] || [ -z "$SERVER_NAME" ]; then
+    echo "Ошибка: нужны host, root_user и server_name"
+    usage
+fi
+
+# Prompt for password
+if [ "$ANONYMOUS" = false ]; then
+    if [ -z "$PASS_NAME" ]; then
+        read -p "Имя для хранения пароля: " PASS_NAME
+    fi
+    read -s -p "Пароль для SSH и sudo: " ROOT_PASS
+    echo
+fi
+
+# Set username for anonymous
+if [ "$ANONYMOUS" = true ]; then
+    NEW_USER=$(generate_random_username)
+fi
+
+# Generate SSH key
+KEY_PATH="$HOME/.ssh/keys/$SERVER_NAME"
+mkdir -p "$HOME/.ssh/keys"
+ssh-keygen -t ed25519 -f "$KEY_PATH" -N "" -C "$SERVER_NAME"
+
+# Read public key
+PUBLIC_KEY=$(cat "$KEY_PATH.pub")
+
+# Generate and store password
+NEW_PASS=""
+if [ "$ANONYMOUS" = false ]; then
+    mkdir -p "$PASS_DIR"
+    NEW_PASS="$(pass generate "$PASS_NAME" 16 | tail -n1)"
+    echo "DEBUG: Generated password: '$NEW_PASS'"
+fi
+
+# Update SSH config
+CONFIG_FILE="$HOME/.ssh/config"
+touch "$CONFIG_FILE"
+chmod 600 "$CONFIG_FILE"
+
+# Remove old entry
+sed -i "/Host $SERVER_NAME/,/^\s*$/d" "$CONFIG_FILE"
+
+# Add new entry
+cat << EOF >> "$CONFIG_FILE"
+
+Host $SERVER_NAME
+    HostName $HOST
+    Port $PORT
+    User $NEW_USER
+    IdentityFile $KEY_PATH
+EOF
+
+# SSH commands
+SSH_COMMANDS=$(cat << ENDSSH
+set -e
+echo '\$PUBLIC_KEY'
+
+# Create user
+echo "\$SUDO_PASS" | sudo -S useradd -m -s /bin/bash -G sudo "\$NEW_USER"
+echo "\$SUDO_PASS" | sudo -S chmod 700 "/home/\$NEW_USER"
+
+# Set password
+if [ -n "\$NEW_PASS" ]; then
+    echo "DEBUG: Setting password for \$NEW_USER: \$NEW_PASS"
+    echo "\$SUDO_PASS" | sudo -S bash -c "echo \"\$NEW_USER:\$NEW_PASS\" | chpasswd"
+fi
+
+# Setup SSH dir
+echo "\$SUDO_PASS" | sudo -S mkdir -p "/home/\$NEW_USER/.ssh"
+echo "\$SUDO_PASS" | sudo -S chmod 700 "/home/\$NEW_USER/.ssh"
+echo "\$SUDO_PASS" | sudo -S touch "/home/\$NEW_USER/.ssh/authorized_keys"
+echo "\$SUDO_PASS" | sudo -S chmod 600 "/home/\$NEW_USER/.ssh/authorized_keys"
+echo "\$SUDO_PASS" | sudo -S chown -R "\$NEW_USER:\$NEW_USER" "/home/\$NEW_USER/.ssh"
+echo "\$SUDO_PASS" | sudo -S bash -c "echo \"\$PUBLIC_KEY\" >> /home/\$NEW_USER/.ssh/authorized_keys"
+
+# Install deps
+echo "\$SUDO_PASS" | sudo -S apt-get update
+echo "\$SUDO_PASS" | sudo -S apt-get install -y git stow
+ENDSSH
+)
+
+# Run SSH commands
+SSHPASS="$ROOT_PASS" sshpass -e ssh -p "$PORT" "$ROOT_USER@$HOST" "SUDO_PASS='$ROOT_PASS' ANONYMOUS=$ANONYMOUS NEW_USER='$NEW_USER' PUBLIC_KEY='$PUBLIC_KEY' NEW_PASS='$NEW_PASS' bash -c '$SSH_COMMANDS'"
+
+ssh "$SERVER_NAME" bash -c "$(cat << ENDSSH
+set -e
+git clone https://github.com/goodhumored/dotfiles
+cd dotfiles
+./init.sh
+ENDSSH
+)"
+
+echo "Настройка завершена!"
+echo "Пользователь: $NEW_USER"
+echo "SSH команда: ssh $SERVER_NAME"
+if [ "$ANONYMOUS" = false ]; then
+    echo "Пароль сохранен в: $PASS_NAME"
+fi
diff --git a/seed_v2.sh b/seed_v2.sh
new file mode 100644
index 0000000..b116b94
--- /dev/null
+++ b/seed_v2.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# --- Константы ---
+DEFAULT_PORT=22
+DEFAULT_USER="goodhumored"
+PASS_DIR="$HOME/.password-store"
+KEYS_DIR="$HOME/.ssh/keys"
+
+# --- Утилиты ---
+require() {
+    if ! command -v "$1" &> /dev/null; then
+        echo "Ошибка: нужна утилита '$1'"
+        exit 1
+    fi
+}
+require sshpass
+require ssh-keygen
+require pass
+
+# --- Параметры ---
+ANONYMOUS=false
+PORT="$DEFAULT_PORT"
+NEW_USER="$DEFAULT_USER"
+
+while [[ $# -gt 0 ]]; do
+    case "$1" in
+        -h|--host) HOST="$2"; shift ;;
+        -p|--port) PORT="$2"; shift ;;
+        -r|--root_user) ROOT_USER="$2"; shift ;;
+        -s|--server_name) SERVER_NAME="$2"; shift ;;
+        -u|--username) NEW_USER="$2"; shift ;;
+        -n|--pass_name) PASS_NAME="$2"; shift ;;
+        -a|--anonymous) ANONYMOUS=true ;;
+        *) echo "Неизвестный параметр: $1"; exit 1 ;;
+    esac
+    shift
+done
+
+[[ -z "${HOST:-}" || -z "${ROOT_USER:-}" || -z "${SERVER_NAME:-}" ]] && {
+    echo "Ошибка: нужны --host, --root_user и --server_name"
+    exit 1
+}
+
+# --- Логика ---
+if $ANONYMOUS; then
+    NEW_USER="user$(tr -dc a-z0-9 </dev/urandom | head -c 8)"
+fi
+
+if [ -z "${PASS_NAME:-}" ] && ! $ANONYMOUS; then
+    read -p "Имя для пароля: " PASS_NAME
+fi
+
+if ! $ANONYMOUS; then
+    read -s -p "Пароль root для подключения: " ROOT_PASS
+    echo
+fi
+
+# Генерация ключа
+mkdir -p "$KEYS_DIR"
+KEY_PATH="$KEYS_DIR/$SERVER_NAME"
+ssh-keygen -t ed25519 -N "" -f "$KEY_PATH" -C "$SERVER_NAME"
+
+# Получение нового пароля
+if ! $ANONYMOUS; then
+    mkdir -p "$PASS_DIR"
+    NEW_PASS=$(pass generate -c "$PASS_NAME" 16 | tail -n1)
+    echo "$NEW_PASS" | pass insert -m "$PASS_NAME"
+else
+    NEW_PASS=""
+fi
+
+# Обновление SSH-конфига
+CONFIG_FILE="$HOME/.ssh/config"
+touch "$CONFIG_FILE"
+chmod 600 "$CONFIG_FILE"
+sed -i "/Host $SERVER_NAME/,/^\s*$/d" "$CONFIG_FILE"
+cat <<EOF >> "$CONFIG_FILE"
+
+Host $SERVER_NAME
+    HostName $HOST
+    Port $PORT
+    User $NEW_USER
+    IdentityFile $KEY_PATH
+EOF
+
+# --- SSH Настройка сервера ---
+SSH_COMMANDS=$(cat <<'EOS'
+set -euo pipefail
+
+sudo useradd -m -s /bin/bash -G sudo "$NEW_USER"
+echo "$NEW_USER:$NEW_PASS" | sudo chpasswd
+
+sudo mkdir -p "/home/$NEW_USER/.ssh"
+sudo chmod 700 "/home/$NEW_USER/.ssh"
+echo "$PUBLIC_KEY" | sudo tee "/home/$NEW_USER/.ssh/authorized_keys" > /dev/null
+sudo chmod 600 "/home/$NEW_USER/.ssh/authorized_keys"
+sudo chown -R "$NEW_USER:$NEW_USER" "/home/$NEW_USER/.ssh"
+
+sudo apt-get update
+sudo apt-get install -y git stow
+
+if [ "$ANONYMOUS" = "false" ]; then
+    sudo -u "$NEW_USER" git clone https://github.com/goodhumored/dotfiles /home/"$NEW_USER"/dotfiles
+    sudo -u "$NEW_USER" bash -c "cd /home/$NEW_USER/dotfiles && ./init.sh"
+fi
+EOS
+)
+
+SSHPASS="$ROOT_PASS" sshpass -e ssh -p "$PORT" "$ROOT_USER@$HOST" \
+    env NEW_USER="$NEW_USER" NEW_PASS="$NEW_PASS" PUBLIC_KEY="$(cat "$KEY_PATH.pub")" ANONYMOUS="$ANONYMOUS" bash -c "$SSH_COMMANDS"
+
+echo "Готово! Новый пользователь: $NEW_USER (SSH: ssh $SERVER_NAME)"