577 lines
18 KiB
Bash
577 lines
18 KiB
Bash
#!/bin/bash
|
|
|
|
# Dotfiles Installation Script - Cross-Platform
|
|
# Optimiert für Fish + Ghostty + Neovim Scratch Setup
|
|
|
|
set -e # Exit on error
|
|
|
|
DOTFILES_DIR="$HOME/gits/dotfiles"
|
|
BACKUP_DIR="$HOME/.config-backup-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# Package manager and OS detection
|
|
PACKAGE_MANAGER=""
|
|
OS=""
|
|
|
|
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
detect_os() {
|
|
log "Detecting operating system..."
|
|
|
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
OS="macos"
|
|
PACKAGE_MANAGER="brew"
|
|
elif [[ -f /etc/arch-release ]]; then
|
|
OS="arch"
|
|
PACKAGE_MANAGER="pacman"
|
|
elif [[ -f /etc/debian_version ]]; then
|
|
OS="debian"
|
|
PACKAGE_MANAGER="apt"
|
|
elif [[ -f /etc/fedora-release ]]; then
|
|
OS="fedora"
|
|
PACKAGE_MANAGER="dnf"
|
|
else
|
|
error "Unsupported operating system"
|
|
exit 1
|
|
fi
|
|
|
|
log "Detected: $OS with $PACKAGE_MANAGER"
|
|
}
|
|
|
|
install_package_manager() {
|
|
case $PACKAGE_MANAGER in
|
|
"brew")
|
|
if ! command -v brew &> /dev/null; then
|
|
log "Installing Homebrew..."
|
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
fi
|
|
;;
|
|
"pacman")
|
|
log "Pacman detected - updating package database..."
|
|
sudo pacman -Sy
|
|
;;
|
|
"apt")
|
|
log "APT detected - updating package database..."
|
|
sudo apt update
|
|
;;
|
|
"dnf")
|
|
log "DNF detected - updating package database..."
|
|
sudo dnf check-update || true
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_package() {
|
|
local package=$1
|
|
log "Installing $package..."
|
|
|
|
case $PACKAGE_MANAGER in
|
|
"brew")
|
|
brew install "$package"
|
|
;;
|
|
"pacman")
|
|
sudo pacman -S --noconfirm "$package"
|
|
;;
|
|
"apt")
|
|
sudo apt install -y "$package"
|
|
;;
|
|
"dnf")
|
|
sudo dnf install -y "$package"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_cask() {
|
|
local app=$1
|
|
log "Installing $app..."
|
|
|
|
case $PACKAGE_MANAGER in
|
|
"brew")
|
|
brew install --cask "$app"
|
|
;;
|
|
"pacman")
|
|
# Use yay for AUR packages
|
|
if command -v yay &> /dev/null; then
|
|
yay -S --noconfirm "$app"
|
|
fi
|
|
;;
|
|
*)
|
|
warn "GUI app installation for $app not supported on $OS"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
header() {
|
|
echo -e "${BLUE}
|
|
╔════════════════════════════════════╗
|
|
║ QA Engineer Dev Setup ║
|
|
║ TypeScript/Playwright + Go ║
|
|
╚════════════════════════════════════╝${NC}"
|
|
}
|
|
|
|
check_prerequisites() {
|
|
log "Checking prerequisites..."
|
|
|
|
detect_os
|
|
install_package_manager
|
|
|
|
# Check dotfiles directory
|
|
if [ ! -d "$DOTFILES_DIR" ]; then
|
|
error "Dotfiles directory not found: $DOTFILES_DIR"
|
|
log "Please ensure your dotfiles are at: $DOTFILES_DIR"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
backup_configs() {
|
|
log "Creating backup..."
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
# Backup existing configs
|
|
[ -d ~/.config/fish ] && cp -r ~/.config/fish "$BACKUP_DIR/"
|
|
[ -d ~/.config/ghostty ] && cp -r ~/.config/ghostty "$BACKUP_DIR/"
|
|
[ -d ~/.config/nvim ] && cp -r ~/.config/nvim "$BACKUP_DIR/"
|
|
[ -d ~/.config/neofetch ] && cp -r ~/.config/neofetch "$BACKUP_DIR/"
|
|
[ -d ~/.config/btop ] && cp -r ~/.config/btop "$BACKUP_DIR/"
|
|
[ -d ~/.config/htop ] && cp -r ~/.config/htop "$BACKUP_DIR/"
|
|
[ -d ~/.config/yabai ] && cp -r ~/.config/yabai "$BACKUP_DIR/"
|
|
[ -d ~/.config/mise ] && cp -r ~/.config/mise "$BACKUP_DIR/"
|
|
[ -f ~/.config/starship.toml ] && cp ~/.config/starship.toml "$BACKUP_DIR/"
|
|
[ -f ~/.tmux.conf ] && cp ~/.tmux.conf "$BACKUP_DIR/"
|
|
[ -d ~/.config/tmux ] && cp -r ~/.config/tmux "$BACKUP_DIR/"
|
|
|
|
log "Backup created: $BACKUP_DIR"
|
|
}
|
|
|
|
install_1password_cli() {
|
|
log "Installing 1Password CLI..."
|
|
|
|
if command -v op &> /dev/null; then
|
|
log "1Password CLI is already installed."
|
|
return
|
|
fi
|
|
|
|
case $OS in
|
|
"macos")
|
|
install_cask 1password-cli
|
|
;;
|
|
"arch")
|
|
if command -v yay &> /dev/null; then
|
|
yay -S --noconfirm 1password-cli
|
|
else
|
|
sudo pacman -S --noconfirm 1password-cli
|
|
fi
|
|
;;
|
|
"debian")
|
|
log "Configuring APT repository for 1Password..."
|
|
curl -sS https://downloads.1password.com/linux/keys/1password.asc | sudo gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg
|
|
echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/amd64 stable main' | sudo tee /etc/apt/sources.list.d/1password.list
|
|
sudo apt update && sudo apt install -y 1password-cli
|
|
;;
|
|
"fedora")
|
|
log "Configuring DNF repository for 1Password..."
|
|
sudo rpm --import https://downloads.1password.com/linux/keys/1password.asc
|
|
sudo sh -c 'echo -e "[1password]\nname=1Password Stable Channel\nbaseurl=https://downloads.1password.com/linux/rpm/stable/\$basearch\nenabled=1\ngpgcheck=1\ngpgkey=https://downloads.1password.com/linux/keys/1password.asc" > /etc/yum.repos.d/1password.repo'
|
|
sudo dnf install -y 1password-cli
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_core_tools() {
|
|
log "Installing core development tools..."
|
|
|
|
case $OS in
|
|
"macos")
|
|
# Essential CLI tools
|
|
install_package git
|
|
install_package neovim
|
|
install_package fish
|
|
install_package starship
|
|
install_package fzf
|
|
install_package ripgrep
|
|
install_package fd
|
|
install_package bat
|
|
install_package btop
|
|
install_package neofetch
|
|
install_package tmux
|
|
|
|
# Development
|
|
install_package go
|
|
install_package python3
|
|
install_package mise
|
|
|
|
# Terminal
|
|
install_cask ghostty
|
|
|
|
# Optional tools
|
|
install_package direnv
|
|
install_package lazygit
|
|
|
|
# Window manager
|
|
log "Tapping koekeishiya/formulae for yabai..."
|
|
brew tap koekeishiya/formulae
|
|
install_package yabai
|
|
;;
|
|
"arch")
|
|
# Check if yay is installed
|
|
if ! command -v yay &> /dev/null; then
|
|
log "Installing yay AUR helper..."
|
|
sudo pacman -S --needed base-devel git
|
|
git clone https://aur.archlinux.org/yay.git /tmp/yay
|
|
cd /tmp/yay && makepkg -si --noconfirm
|
|
cd - && rm -rf /tmp/yay
|
|
fi
|
|
|
|
# Install via yay
|
|
yay -S --noconfirm \
|
|
git neovim fish starship fzf ripgrep fd bat btop neofetch tmux \
|
|
go python mise \
|
|
ghostty direnv lazygit
|
|
;;
|
|
"debian"|"fedora")
|
|
# Basic tools
|
|
install_package git
|
|
install_package fish
|
|
install_package fzf
|
|
install_package ripgrep
|
|
install_package fd-find
|
|
install_package bat
|
|
install_package neofetch
|
|
install_package tmux
|
|
|
|
# Neovim (from source if needed)
|
|
if ! command -v nvim &> /dev/null; then
|
|
warn "Installing Neovim from source..."
|
|
install_neovim_from_source
|
|
fi
|
|
|
|
# Starship
|
|
curl -sS https://starship.rs/install.sh | sh
|
|
|
|
# Development
|
|
install_package golang
|
|
install_package python3
|
|
|
|
# Install mise
|
|
if ! command -v mise &> /dev/null; then
|
|
log "Installing mise version manager..."
|
|
curl https://mise.run | sh
|
|
fi
|
|
|
|
warn "Ghostty needs manual installation on $OS"
|
|
;;
|
|
esac
|
|
|
|
install_1password_cli
|
|
|
|
log "Core tools installed!"
|
|
}
|
|
|
|
install_neovim_from_source() {
|
|
log "Building Neovim from source..."
|
|
|
|
# Install build dependencies
|
|
case $OS in
|
|
"debian")
|
|
sudo apt install -y ninja-build gettext cmake unzip curl build-essential
|
|
;;
|
|
"fedora")
|
|
sudo dnf install -y ninja-build cmake gcc make unzip gettext curl
|
|
;;
|
|
esac
|
|
|
|
# Clone and build
|
|
cd /tmp
|
|
git clone https://github.com/neovim/neovim
|
|
cd neovim
|
|
git checkout stable
|
|
make CMAKE_BUILD_TYPE=Release
|
|
sudo make install
|
|
cd ~
|
|
rm -rf /tmp/neovim
|
|
|
|
log "Neovim installed from source"
|
|
}
|
|
|
|
setup_fish() {
|
|
log "Setting up Fish shell..."
|
|
|
|
# Install Fisher (Fish plugin manager)
|
|
if ! fish -c "type -q fisher" 2>/dev/null; then
|
|
log "Installing Fisher..."
|
|
fish -c "curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher"
|
|
fi
|
|
|
|
# Remove existing fish config (if not symlink)
|
|
if [ ! -L ~/.config/fish ] && [ -d ~/.config/fish ]; then
|
|
rm -rf ~/.config/fish
|
|
fi
|
|
|
|
# Create symlink to dotfiles
|
|
ln -sf "$DOTFILES_DIR/.config/fish" ~/.config/fish
|
|
|
|
# Install Fish plugins
|
|
log "Installing Fish plugins..."
|
|
fish -c "fisher install jorgebucaran/nvm.fish" || true
|
|
fish -c "fisher install PatrickF1/fzf.fish" || true
|
|
fish -c "fisher install franciscolourenco/done" || true
|
|
|
|
# Set as default shell
|
|
local fish_path=$(which fish)
|
|
if ! grep -q "$fish_path" /etc/shells 2>/dev/null; then
|
|
echo "$fish_path" | sudo tee -a /etc/shells
|
|
fi
|
|
|
|
if [ "$SHELL" != "$fish_path" ]; then
|
|
log "Setting Fish as default shell..."
|
|
chsh -s "$fish_path"
|
|
fi
|
|
|
|
log "Fish shell configured!"
|
|
}
|
|
|
|
setup_neovim() {
|
|
log "Setting up Neovim..."
|
|
|
|
# Remove existing nvim config (if not symlink)
|
|
if [ ! -L ~/.config/nvim ] && [ -d ~/.config/nvim ]; then
|
|
rm -rf ~/.config/nvim
|
|
fi
|
|
|
|
# Create symlink
|
|
ln -sf "$DOTFILES_DIR/.config/nvim" ~/.config/nvim
|
|
|
|
log "Neovim configured! Run 'nvim' to install plugins on first start."
|
|
}
|
|
|
|
setup_other_configs() {
|
|
log "Setting up other configurations..."
|
|
|
|
# Ghostty
|
|
if [ -d "$DOTFILES_DIR/.config/ghostty" ]; then
|
|
rm -rf ~/.config/ghostty
|
|
ln -sf "$DOTFILES_DIR/.config/ghostty" ~/.config/ghostty
|
|
fi
|
|
|
|
# Starship
|
|
ln -sf "$DOTFILES_DIR/.config/starship.toml" ~/.config/starship.toml
|
|
|
|
# Neofetch
|
|
if [ -d "$DOTFILES_DIR/.config/neofetch" ]; then
|
|
rm -rf ~/.config/neofetch
|
|
ln -sf "$DOTFILES_DIR/.config/neofetch" ~/.config/neofetch
|
|
fi
|
|
|
|
# Btop
|
|
if [ -d "$DOTFILES_DIR/.config/btop" ]; then
|
|
mkdir -p ~/.config/btop
|
|
ln -sf "$DOTFILES_DIR/.config/btop/btop.conf" ~/.config/btop/btop.conf
|
|
[ -d "$DOTFILES_DIR/.config/btop/themes" ] && ln -sf "$DOTFILES_DIR/.config/btop/themes" ~/.config/btop/themes
|
|
fi
|
|
|
|
# Htop (fallback wenn btop abstürzt)
|
|
if [ -d "$DOTFILES_DIR/.config/htop" ]; then
|
|
mkdir -p ~/.config/htop
|
|
ln -sf "$DOTFILES_DIR/.config/htop/htoprc" ~/.config/htop/htoprc
|
|
fi
|
|
|
|
# Yabai (macOS only)
|
|
if [ "$OS" = "macos" ] && [ -d "$DOTFILES_DIR/.config/yabai" ]; then
|
|
rm -rf ~/.config/yabai
|
|
ln -sf "$DOTFILES_DIR/.config/yabai" ~/.config/yabai
|
|
fi
|
|
|
|
# Mise
|
|
if [ -d "$DOTFILES_DIR/.config/mise" ]; then
|
|
rm -rf ~/.config/mise
|
|
ln -sf "$DOTFILES_DIR/.config/mise" ~/.config/mise
|
|
fi
|
|
|
|
# Tmux
|
|
if [ -d "$DOTFILES_DIR/.config/tmux" ]; then
|
|
rm -rf ~/.config/tmux
|
|
ln -sf "$DOTFILES_DIR/.config/tmux" ~/.config/tmux
|
|
# Symlink tmux.conf to home directory for backward compatibility
|
|
rm -f ~/.tmux.conf
|
|
ln -sf "$DOTFILES_DIR/.config/tmux/tmux.conf" ~/.tmux.conf
|
|
fi
|
|
|
|
log "Configurations symlinked!"
|
|
}
|
|
|
|
install_playwright() {
|
|
log "Installing Playwright..."
|
|
|
|
local MISE_CMD="mise"
|
|
if [ -f "$HOME/.local/bin/mise" ]; then
|
|
MISE_CMD="$HOME/.local/bin/mise"
|
|
fi
|
|
|
|
if command -v "$MISE_CMD" &> /dev/null; then
|
|
log "Using mise node runtime for global packages..."
|
|
"$MISE_CMD" exec -- npm install -g @playwright/test
|
|
"$MISE_CMD" exec -- npx playwright install
|
|
log "Playwright installed!"
|
|
elif command -v npm &> /dev/null; then
|
|
log "Using system npm..."
|
|
npm install -g @playwright/test
|
|
npx playwright install
|
|
log "Playwright installed!"
|
|
else
|
|
warn "npm / mise not found, skipping Playwright"
|
|
fi
|
|
}
|
|
|
|
install_optional_ai_tools() {
|
|
# Check if stdin is a terminal (tty) to avoid hanging in automated setups
|
|
if [ ! -t 0 ]; then
|
|
log "Non-interactive shell detected, skipping optional AI tools installation."
|
|
return 0
|
|
fi
|
|
|
|
log "Checking Google Antigravity CLI status..."
|
|
if ! command -v agy &> /dev/null; then
|
|
warn "Google Antigravity CLI (agy) is not installed in the PATH."
|
|
log "To install it, run the official installer: curl -sS https://antigravity.google/install.sh | bash"
|
|
else
|
|
log "Google Antigravity CLI (agy) is already installed."
|
|
fi
|
|
|
|
echo -e "\n${YELLOW}=== Optional AI CLI Tools ===${NC}"
|
|
|
|
# Prompt helper
|
|
prompt_yes_no() {
|
|
local prompt_msg=$1
|
|
local response
|
|
read -p "$prompt_msg [y/N]: " response
|
|
case "$response" in
|
|
[yY][eE][sS]|[yY])
|
|
return 0
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# 1. Claude Code
|
|
if prompt_yes_no "Do you want to install Claude Code (@anthropic-ai/claude-code)?"; then
|
|
log "Installing Claude Code..."
|
|
local MISE_CMD="mise"
|
|
[ -f "$HOME/.local/bin/mise" ] && MISE_CMD="$HOME/.local/bin/mise"
|
|
if command -v "$MISE_CMD" &> /dev/null; then
|
|
"$MISE_CMD" exec -- npm install -g @anthropic-ai/claude-code
|
|
elif command -v npm &> /dev/null; then
|
|
npm install -g @anthropic-ai/claude-code
|
|
else
|
|
warn "npm / mise not found, cannot install Claude Code."
|
|
fi
|
|
fi
|
|
|
|
# 2. Simon Willison's LLM CLI (supports OpenAI, Claude, DeepSeek via plugins)
|
|
if prompt_yes_no "Do you want to install Simon Willison's LLM CLI (supports ChatGPT, Claude, DeepSeek)?"; then
|
|
log "Installing LLM CLI..."
|
|
case $PACKAGE_MANAGER in
|
|
"brew")
|
|
brew install llm
|
|
;;
|
|
*)
|
|
if command -v pip3 &> /dev/null; then
|
|
pip3 install llm
|
|
elif command -v pip &> /dev/null; then
|
|
pip install llm
|
|
else
|
|
warn "pip not found, cannot install llm cli."
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Install plugins if llm is installed
|
|
if command -v llm &> /dev/null; then
|
|
log "Installing plugins for llm (llm-claude, llm-gpt3, llm-deepseek)..."
|
|
llm install llm-claude
|
|
llm install llm-gpt3
|
|
llm install llm-deepseek || true
|
|
fi
|
|
fi
|
|
|
|
# 3. tgpt (Zero-config terminal GPT chat client)
|
|
if prompt_yes_no "Do you want to install tgpt (Zero-config Terminal ChatGPT)?"; then
|
|
log "Installing tgpt..."
|
|
case $PACKAGE_MANAGER in
|
|
"brew")
|
|
brew install tgpt
|
|
;;
|
|
*)
|
|
curl -sSL https://raw.githubusercontent.com/aandrew-me/tgpt/main/install.sh | bash -s -- -y
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# 4. Ollama (for running DeepSeek locally offline)
|
|
if prompt_yes_no "Do you want to install Ollama (to run DeepSeek and other models locally)?"; then
|
|
log "Installing Ollama..."
|
|
case $OS in
|
|
"macos")
|
|
install_cask ollama
|
|
;;
|
|
*)
|
|
curl -fsSL https://ollama.com/install.sh | sh
|
|
;;
|
|
esac
|
|
log "To run DeepSeek locally after Ollama is running, execute: ollama run deepseek-coder"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
header
|
|
check_prerequisites
|
|
backup_configs
|
|
install_core_tools
|
|
setup_fish
|
|
setup_neovim
|
|
setup_other_configs
|
|
|
|
# Install runtimes via mise
|
|
local MISE_CMD="mise"
|
|
if [ -f "$HOME/.local/bin/mise" ]; then
|
|
MISE_CMD="$HOME/.local/bin/mise"
|
|
fi
|
|
if command -v "$MISE_CMD" &> /dev/null; then
|
|
log "Installing language runtimes configured in mise..."
|
|
"$MISE_CMD" install
|
|
fi
|
|
|
|
install_playwright
|
|
install_optional_ai_tools
|
|
|
|
echo -e "${GREEN}
|
|
╔══════════════════════════════════════════════════════════╗
|
|
║ Setup Complete! ║
|
|
║ ║
|
|
║ ✓ $OS detected ║
|
|
║ ✓ Fish shell configured ║
|
|
║ ✓ Neovim (Scratch) configured ║
|
|
║ ✓ Ghostty terminal configured ║
|
|
║ ✓ Development tools installed ║
|
|
║ ✓ Dotfiles symlinked ║
|
|
║ ║
|
|
║ Next steps: ║
|
|
║ 1. Restart your terminal ║
|
|
║ 2. Run 'nvim' to install plugins ║
|
|
║ 3. Enjoy your setup! ║
|
|
║ ║
|
|
║ Backup: $BACKUP_DIR ║
|
|
╚══════════════════════════════════════════════════════════╝${NC}"
|
|
}
|
|
|
|
main "$@"
|