13  Configuración y Personalización

Este capítulo final cubre la configuración avanzada del entorno de trabajo, personalización profunda de herramientas y creación de un sistema altamente productivo y eficiente.

13.1 Configuración del Shell

13.1.1 Configuración de zsh avanzada

Archivo ~/.zshrc optimizado:

# ~/.zshrc - Configuración avanzada

# History
HISTSIZE=50000
SAVEHIST=50000
HISTFILE=~/.zsh_history
setopt HIST_VERIFY
setopt SHARE_HISTORY
setopt APPEND_HISTORY
setopt INC_APPEND_HISTORY
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_IGNORE_SPACE

# Options
setopt AUTO_CD
setopt CORRECT
setopt CORRECT_ALL

# Completions
autoload -Uz compinit
compinit

# Case insensitive completion
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'

# Initialize tools
eval "$(starship init zsh)"
eval "$(direnv hook zsh)"
eval "$(zoxide init zsh)"
eval $(thefuck --alias fix)

# Path additions
export PATH="/opt/homebrew/bin:$PATH"
export PATH="$HOME/.local/bin:$PATH"

# Environment variables
export EDITOR="code --wait"
export VISUAL="$EDITOR"
export PAGER="bat"
export MANPAGER="sh -c 'col -bx | bat -l man -p'"

# FZF configuration
export FZF_DEFAULT_COMMAND='rg --files --hidden --follow --glob "!.git/*"'
export FZF_DEFAULT_OPTS='
    --height 40% 
    --layout=reverse 
    --border
    --preview "bat --color=always --style=header,grid --line-range :300 {}"
'

# Aliases
alias ls='eza'
alias ll='eza -la --git'
alias lt='eza --tree'
alias cat='bat'
alias grep='rg'
alias find='fd'
alias du='dust'
alias df='duf'
alias ps='procs'
alias top='htop'

# Git aliases
alias g='git'
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git pull'
alias gco='git checkout'
alias gb='git branch'
alias gm='git merge'
alias gd='git diff'
alias glog='git log --oneline --graph'

# Functions
# Quick CD and list
cl() {
    cd "$1" && ll
}

# Make directory and CD into it
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Find and edit files
fe() {
    local files
    files=$(fzf --multi --preview 'bat --color=always {}') && ${EDITOR:-vim} "${files[@]}"
}

# Kill process by name
fkill() {
    local pid
    pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')
    if [ "x$pid" != "x" ]; then
        echo $pid | xargs kill -${1:-9}
    fi
}

# Git commit with conventional commits
gci() {
    local type="$1"
    local scope="$2"
    local message="$3"
    
    if [ -z "$type" ] || [ -z "$message" ]; then
        echo "Uso: gci <type> [scope] <message>"
        echo "Types: feat, fix, docs, style, refactor, test, chore"
        return 1
    fi
    
    if [ -n "$scope" ]; then
        git commit -m "${type}(${scope}): ${message}"
    else
        git commit -m "${type}: ${message}"
    fi
}

# Project initialization
init_project() {
    local project_type="$1"
    local project_name="$2"
    
    if [ -z "$project_type" ] || [ -z "$project_name" ]; then
        echo "Uso: init_project <type> <name>"
        echo "Types: python, node, go, rust"
        return 1
    fi
    
    mkdir "$project_name" && cd "$project_name"
    
    case "$project_type" in
        python)
            python3 -m venv .venv
            echo "source .venv/bin/activate" > .envrc
            echo ".venv/" > .gitignore
            echo "*.pyc" >> .gitignore
            echo "__pycache__/" >> .gitignore
            direnv allow
            ;;
        node)
            npm init -y
            echo "node_modules/" > .gitignore
            echo ".env" >> .gitignore
            echo "dist/" >> .gitignore
            ;;
        go)
            go mod init "$project_name"
            echo "# $project_name" > README.md
            ;;
        rust)
            cargo init
            ;;
    esac
    
    git init
    touch README.md
    git add .
    git commit -m "chore: initial commit"
}

# Load local configuration if exists
[ -f ~/.zshrc.local ] && source ~/.zshrc.local

13.1.2 Configuración de Starship

Archivo ~/.config/starship.toml:

# Starship configuration

format = """
$username\
$hostname\
$directory\
$git_branch\
$git_state\
$git_status\
$git_metrics\
$fill\
$nodejs\
$python\
$rust\
$golang\
$package\
$docker_context\
$time\
$line_break\
$character"""

[fill]
symbol = " "

[directory]
style = "blue"
truncation_length = 4
truncation_symbol = "…/"

[character]
success_symbol = "[❯](purple)"
error_symbol = "[❯](red)"
vicmd_symbol = "[❮](green)"

[git_branch]
symbol = "🌱 "
truncation_length = 15
truncation_symbol = "…"

[git_status]
ahead = "⇡${count}"
diverged = "⇕⇡${ahead_count}⇣${behind_count}"
behind = "⇣${count}"
conflicted = "🏳"
untracked = "🤷"
stashed = "📦"
modified = "📝"
staged = '[++\($count\)](green)'
renamed = "👅"
deleted = "🗑"

[nodejs]
symbol = "⬢ "
detect_files = ["package.json", ".nvmrc"]
detect_folders = ["node_modules"]

[python]
symbol = "🐍 "
detect_extensions = ["py"]
detect_files = ["requirements.txt", ".python-version", "pyproject.toml"]

[rust]
symbol = "🦀 "
detect_extensions = ["rs"]
detect_files = ["Cargo.toml"]

[golang]
symbol = "🐹 "
detect_extensions = ["go"]
detect_files = ["go.mod", "go.sum", "glide.yaml"]

[docker_context]
symbol = "🐳 "
detect_files = ["docker-compose.yml", "docker-compose.yaml", "Dockerfile"]

[time]
disabled = false
format = '🕙[\[ $time \]]($style) '
time_format = "%T"
utc_time_offset = "local"

[package]
symbol = "📦 "

13.2 Configuración de herramientas específicas

13.2.1 Git configuración avanzada

#!/bin/bash
# setup-git.sh - Configuración avanzada de Git

setup_git_config() {
    echo "🔧 Configurando Git..."
    
    # Configuración básica
    git config --global init.defaultBranch main
    git config --global pull.rebase false
    git config --global push.default simple
    git config --global core.autocrlf input
    git config --global core.editor "code --wait"
    
    # Aliases útiles
    git config --global alias.st status
    git config --global alias.co checkout
    git config --global alias.br branch
    git config --global alias.ci commit
    git config --global alias.unstage 'reset HEAD --'
    git config --global alias.last 'log -1 HEAD'
    git config --global alias.visual '!gitk'
    git config --global alias.graph 'log --graph --pretty=format:"%h -%d %s (%cr) <%an>" --abbrev-commit'
    git config --global alias.conflicts 'diff --name-only --diff-filter=U'
    
    # Configuración de merge
    git config --global merge.tool vscode
    git config --global mergetool.vscode.cmd 'code --wait $MERGED'
    git config --global diff.tool vscode
    git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
    
    # Configuración de colores
    git config --global color.ui auto
    git config --global color.branch.current "yellow reverse"
    git config --global color.branch.local yellow
    git config --global color.branch.remote green
    git config --global color.diff.meta "yellow bold"
    git config --global color.diff.frag "magenta bold"
    git config --global color.diff.old "red bold"
    git config --global color.diff.new "green bold"
    git config --global color.status.added yellow
    git config --global color.status.changed green
    git config --global color.status.untracked cyan
}

setup_git_hooks() {
    echo "🪝 Configurando Git hooks..."
    
    # Hook de pre-commit
    cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# Pre-commit hook

# Verificar que no hay debuggers
if grep -r "debugger\|console\.log\|pdb\.set_trace" --include="*.js" --include="*.py" .; then
    echo "❌ Debuggers encontrados. Remueve antes de commitear."
    exit 1
fi

# Ejecutar linting si existe
if [ -f "package.json" ]; then
    npm run lint 2>/dev/null || true
fi

# Ejecutar tests si existen
if [ -f "package.json" ]; then
    npm test 2>/dev/null || true
fi
EOF

    chmod +x .git/hooks/pre-commit
}

setup_git_config
[ -d ".git" ] && setup_git_hooks

13.2.2 Configuración de herramientas de desarrollo

#!/bin/bash
# setup-dev-tools.sh

setup_bat() {
    echo "🦇 Configurando bat..."
    
    mkdir -p ~/.config/bat
    cat > ~/.config/bat/config << 'EOF'
--theme="gruvbox-dark"
--style="numbers,changes,header"
--paging=auto
--wrap=never
EOF
}

setup_ripgrep() {
    echo "🔍 Configurando ripgrep..."
    
    cat > ~/.ripgreprc << 'EOF'
--max-columns=150
--max-columns-preview
--smart-case
--hidden
--glob=!.git/*
--glob=!node_modules/*
--glob=!dist/*
--glob=!build/*
--colors=line:none
--colors=line:style:bold
EOF
    
    export RIPGREP_CONFIG_PATH="$HOME/.ripgreprc"
}

setup_fzf() {
    echo "🔭 Configurando fzf..."
    
    export FZF_DEFAULT_COMMAND='rg --files --hidden --follow --glob "!.git/*"'
    export FZF_DEFAULT_OPTS='
        --height 40% 
        --layout=reverse 
        --border
        --preview "bat --color=always --style=header,grid --line-range :300 {}"
        --bind "ctrl-/:change-preview-window(down|hidden|)"
        --bind "ctrl-y:execute-silent(echo {} | pbcopy)+abort"
        --color=bg+:#414559,bg:#303446,spinner:#f2d5cf,hl:#e78284
        --color=fg:#c6d0f5,header:#e78284,info:#ca9ee6,pointer:#f2d5cf
        --color=marker:#f2d5cf,fg+:#c6d0f5,prompt:#ca9ee6,hl+:#e78284
    '
}

setup_all() {
    setup_bat
    setup_ripgrep
    setup_fzf
    
    echo "✅ Configuración de herramientas completada"
}

setup_all

13.3 Scripts de productividad

13.3.1 Sistema de templates de proyecto

#!/bin/bash
# project-templates.sh

TEMPLATES_DIR="$HOME/.project-templates"

create_templates() {
    mkdir -p "$TEMPLATES_DIR"
    
    # Template Python
    mkdir -p "$TEMPLATES_DIR/python"
    cat > "$TEMPLATES_DIR/python/.envrc" << 'EOF'
export PYTHONPATH="$PWD/src:$PYTHONPATH"
export VIRTUAL_ENV="$PWD/.venv"
export PATH="$VIRTUAL_ENV/bin:$PATH"

if [ -d "$VIRTUAL_ENV" ]; then
    source "$VIRTUAL_ENV/bin/activate"
fi
EOF

    cat > "$TEMPLATES_DIR/python/pyproject.toml" << 'EOF'
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "PROJECT_NAME"
version = "0.1.0"
description = ""
authors = [{name = "Your Name", email = "your@email.com"}]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.8"
dependencies = []

[project.optional-dependencies]
dev = [
    "pytest",
    "black",
    "isort",
    "flake8",
    "mypy"
]

[tool.black]
line-length = 88
target-version = ['py38']

[tool.isort]
profile = "black"
line_length = 88
EOF

    # Template Node.js
    mkdir -p "$TEMPLATES_DIR/nodejs"
    cat > "$TEMPLATES_DIR/nodejs/.envrc" << 'EOF'
export NODE_ENV=development
export PATH="$PWD/node_modules/.bin:$PATH"
EOF

    cat > "$TEMPLATES_DIR/nodejs/package.json" << 'EOF'
{
  "name": "PROJECT_NAME",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier --write ."
  },
  "keywords": [],
  "author": "Your Name",
  "license": "MIT",
  "devDependencies": {
    "eslint": "^8.0.0",
    "prettier": "^2.0.0",
    "nodemon": "^2.0.0",
    "jest": "^28.0.0"
  }
}
EOF

    echo "📁 Templates creados en $TEMPLATES_DIR"
}

new_project() {
    local template="$1"
    local name="$2"
    
    if [ -z "$template" ] || [ -z "$name" ]; then
        echo "Uso: new_project <template> <name>"
        echo "Templates disponibles:"
        ls "$TEMPLATES_DIR" 2>/dev/null || echo "No hay templates (ejecuta: create_templates)"
        return 1
    fi
    
    if [ ! -d "$TEMPLATES_DIR/$template" ]; then
        echo "❌ Template '$template' no encontrado"
        return 1
    fi
    
    if [ -d "$name" ]; then
        echo "❌ Directorio '$name' ya existe"
        return 1
    fi
    
    echo "🚀 Creando proyecto '$name' desde template '$template'..."
    
    # Copiar template
    cp -r "$TEMPLATES_DIR/$template" "$name"
    cd "$name"
    
    # Reemplazar PROJECT_NAME en archivos
    if command -v sed >/dev/null; then
        find . -type f -name "*.json" -o -name "*.toml" -o -name "*.md" | \
        xargs sed -i.bak "s/PROJECT_NAME/$name/g"
        find . -name "*.bak" -delete
    fi
    
    # Inicializar git
    git init
    git add .
    git commit -m "chore: initial commit from template"
    
    # Configurar según tipo de proyecto
    case "$template" in
        python)
            python3 -m venv .venv
            direnv allow
            ;;
        nodejs)
            npm install
            direnv allow
            ;;
    esac
    
    echo "✅ Proyecto '$name' creado exitosamente"
    echo "📁 Ubicación: $(pwd)"
}

case "${1:-help}" in
    create-templates)
        create_templates
        ;;
    new)
        new_project "$2" "$3"
        ;;
    list)
        echo "Templates disponibles:"
        ls "$TEMPLATES_DIR" 2>/dev/null || echo "No hay templates"
        ;;
    *)
        echo "Project Templates Manager"
        echo "Uso: $0 <comando> [argumentos]"
        echo ""
        echo "Comandos:"
        echo "  create-templates     - Crear templates básicos"
        echo "  new <template> <name> - Crear proyecto desde template"
        echo "  list                 - Listar templates disponibles"
        ;;
esac

13.3.2 Backup automatizado

#!/bin/bash
# auto-backup.sh - Sistema de backup automatizado

BACKUP_CONFIG="$HOME/.config/auto-backup/config.json"
BACKUP_LOG="$HOME/.local/log/backup.log"

init_backup() {
    mkdir -p "$(dirname "$BACKUP_CONFIG")"
    mkdir -p "$(dirname "$BACKUP_LOG")"
    
    cat > "$BACKUP_CONFIG" << 'EOF'
{
    "directories": [
        "~/Documents",
        "~/Projects",
        "~/.config"
    ],
    "exclude": [
        "*.tmp",
        "*.log",
        "node_modules",
        ".git",
        "dist",
        "build"
    ],
    "destinations": {
        "local": "~/Backups",
        "remote": "user@server.com:/backups"
    },
    "retention": {
        "daily": 7,
        "weekly": 4,
        "monthly": 12
    }
}
EOF
    
    echo "Configuración de backup creada en: $BACKUP_CONFIG"
}

perform_backup() {
    local destination_type="${1:-local}"
    local timestamp=$(date +%Y%m%d_%H%M%S)
    
    log_message() {
        echo "[$(date)] $1" | tee -a "$BACKUP_LOG"
    }
    
    log_message "Iniciando backup ($destination_type)..."
    
    # Leer configuración
    local directories=$(jq -r '.directories[]' "$BACKUP_CONFIG")
    local excludes=$(jq -r '.exclude[]' "$BACKUP_CONFIG" | sed 's/^/--exclude=/')
    local destination=$(jq -r ".destinations.$destination_type" "$BACKUP_CONFIG")
    
    # Expandir tilde en destination
    destination=$(eval echo "$destination")
    
    # Crear directorio de backup
    local backup_dir="$destination/backup_$timestamp"
    mkdir -p "$backup_dir"
    
    # Realizar backup de cada directorio
    echo "$directories" | while read -r dir; do
        if [ -n "$dir" ]; then
            expanded_dir=$(eval echo "$dir")
            if [ -d "$expanded_dir" ]; then
                log_message "Backing up: $expanded_dir"
                
                rsync -av --delete \
                      $excludes \
                      "$expanded_dir/" \
                      "$backup_dir/$(basename "$expanded_dir")/"
                      
                if [ $? -eq 0 ]; then
                    log_message "✅ Success: $expanded_dir"
                else
                    log_message "❌ Error: $expanded_dir"
                fi
            fi
        fi
    done
    
    # Comprimir backup
    log_message "Comprimiendo backup..."
    cd "$(dirname "$backup_dir")"
    tar -czf "backup_$timestamp.tar.gz" "backup_$timestamp"
    rm -rf "backup_$timestamp"
    
    # Limpieza de backups antiguos
    cleanup_old_backups "$destination"
    
    log_message "Backup completado: backup_$timestamp.tar.gz"
}

cleanup_old_backups() {
    local backup_dir="$1"
    local daily_retention=$(jq -r '.retention.daily' "$BACKUP_CONFIG")
    
    # Eliminar backups antiguos (mantener solo los últimos N)
    cd "$backup_dir"
    ls -t backup_*.tar.gz | tail -n +$((daily_retention + 1)) | xargs rm -f
}

case "${1:-help}" in
    init)
        init_backup
        ;;
    backup)
        [ ! -f "$BACKUP_CONFIG" ] && init_backup
        perform_backup "${2:-local}"
        ;;
    logs)
        tail -f "$BACKUP_LOG"
        ;;
    *)
        echo "Auto Backup System"
        echo "Uso: $0 <comando> [tipo]"
        echo ""
        echo "Comandos:"
        echo "  init            - Crear configuración inicial"
        echo "  backup [local]  - Realizar backup local"
        echo "  backup remote   - Realizar backup remoto"
        echo "  logs            - Ver logs de backup"
        ;;
esac

13.4 Instalación completa del entorno

#!/bin/bash
# setup-complete-environment.sh - Instalación completa

setup_homebrew_tools() {
    echo "🍺 Verificando herramientas de Homebrew..."
    
    tools=(
        "bat" "eza" "ripgrep" "fzf" "zoxide" "starship"
        "direnv" "tealdeer" "thefuck" "git" "gh" "node"
        "ffmpeg" "imagemagick" "pandoc" "glow" "htop"
        "fastfetch" "cowsay" "jq" "curl" "wget" "aria2"
    )
    
    for tool in "${tools[@]}"; do
        if ! command -v "$tool" >/dev/null; then
            echo "Instalando $tool..."
            brew install "$tool"
        fi
    done
}

setup_dotfiles() {
    echo "📁 Configurando dotfiles..."
    
    # Backup de configuraciones existentes
    [ -f ~/.zshrc ] && cp ~/.zshrc ~/.zshrc.backup
    [ -f ~/.gitconfig ] && cp ~/.gitconfig ~/.gitconfig.backup
    
    # Aplicar configuraciones
    ./setup-git.sh
    ./setup-dev-tools.sh
    
    # Copiar archivos de configuración
    cp .zshrc ~/.zshrc
    cp starship.toml ~/.config/starship.toml
}

setup_directories() {
    echo "📂 Creando estructura de directorios..."
    
    mkdir -p ~/Projects/{personal,work,forks}
    mkdir -p ~/Scripts
    mkdir -p ~/.local/{bin,log}
    mkdir -p ~/.config/{auto-backup,smart-monitor}
}

setup_scripts() {
    echo "📜 Instalando scripts útiles..."
    
    # Copiar scripts a ~/.local/bin
    cp *.sh ~/.local/bin/
    chmod +x ~/.local/bin/*.sh
    
    # Crear enlaces simbólicos para facilidad de uso
    ln -sf ~/.local/bin/project-templates.sh ~/.local/bin/new-project
    ln -sf ~/.local/bin/auto-backup.sh ~/.local/bin/backup
    ln -sf ~/.local/bin/git-workflow.sh ~/.local/bin/git-flow
}

finalize_setup() {
    echo "🎯 Finalizando configuración..."
    
    # Actualizar tealdeer
    tldr --update
    
    # Configurar direnv para que funcione inmediatamente
    eval "$(direnv hook zsh)"
    
    # Mensaje final
    cat << 'EOF'

🎉 ¡Configuración completada!

Pasos siguientes:
1. Reinicia tu terminal o ejecuta: source ~/.zshrc
2. Configura Git con tu información: git config --global user.name "Tu Nombre"
3. Ejecuta: gh auth login para configurar GitHub CLI
4. Crea tu primer proyecto: new-project python mi-proyecto

Comandos útiles instalados:
- new-project <tipo> <nombre> - Crear proyecto desde template
- backup [local|remote]       - Realizar backup
- git-flow <comando>          - Workflow avanzado de Git

¡Disfruta tu nuevo entorno de desarrollo! 🚀
EOF
}

main() {
    echo "🔧 Configurando entorno completo de desarrollo..."
    
    setup_homebrew_tools
    setup_dotfiles
    setup_directories
    setup_scripts
    finalize_setup
}

main "$@"
Tips finales de configuración
  • Mantén un repositorio Git con tus dotfiles para sincronizar entre máquinas
  • Documenta tus configuraciones personalizadas en un README
  • Haz backups regulares de tus configuraciones importantes
  • Experimenta con nuevas herramientas pero mantén tu setup estable para trabajo
Mantenimiento del entorno
  • Actualiza regularmente tus herramientas: brew update && brew upgrade
  • Revisa y limpia configuraciones que ya no uses
  • Mantén scripts actualizados y funcionales
  • Considera usar herramientas como mackup para backup de configuraciones

13.5 Conclusión

Este libro ha cubierto un ecosistema completo de herramientas CLI modernas que transformarán tu productividad en terminal. Desde navegación básica hasta workflows complejos de desarrollo, tienes ahora una base sólida para construir un entorno de trabajo eficiente y personalizado.

13.5.1 Lo que has aprendido:

Navegación eficiente con eza, tree, ranger y zoxide
Gestión avanzada de archivos con rsync y herramientas de renombrado
Búsqueda ultrarrápida con ripgrep, fzf y jq
Desarrollo moderno con git, gh y node
Procesamiento multimedia con ffmpeg, yt-dlp e imagemagick
Networking con curl, httpie y herramientas de descarga
Monitoreo con htop y fastfetch
Documentación con bat, pandoc y glow
Utilidades como tealdeer, thefuck y starship
Workflows complejos combinando múltiples herramientas
Configuración completa de un entorno productivo

13.5.2 Próximos pasos:

  1. Practica regularmente - La muscle memory es clave
  2. Personaliza según tus necesidades específicas
  3. Automatiza tareas repetitivas con scripts
  4. Comparte tu conocimiento con otros desarrolladores
  5. Mantente actualizado con nuevas herramientas y versiones

¡El dominio de estas herramientas CLI te convertirá en un desarrollador más eficiente y productivo! 🚀