mirror of
https://github.com/marcogll/mac_vntySet.git
synced 2026-01-13 21:35:15 +00:00
Se modifica la función 'brew_ensure_cask' para detectar y omitir la instalación de casks de fuentes que ya existen en el sistema. Esto evita que el script se detenga con un error cuando una fuente ya está presente, lo que hace que el proceso de instalación sea más robusto.
489 lines
12 KiB
Bash
489 lines
12 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_PATH=${BASH_SOURCE[0]:-$0}
|
|
if [[ "$SCRIPT_PATH" == "bash" || "$SCRIPT_PATH" == "-bash" ]]; then
|
|
SCRIPT_DIR="$PWD"
|
|
else
|
|
SCRIPT_DIR=$(cd "$(dirname "$SCRIPT_PATH")" >/dev/null 2>&1 && pwd -P) || SCRIPT_DIR="$PWD"
|
|
fi
|
|
|
|
LOG_DIR="$SCRIPT_DIR/.logs"
|
|
mkdir -p "$LOG_DIR"
|
|
LOG_FILE="$LOG_DIR/vanity-$(date +%Y%m%d-%H%M%S).log"
|
|
touch "$LOG_FILE"
|
|
exec > >(tee -a "$LOG_FILE") 2>&1
|
|
echo "Log de instalación: $LOG_FILE"
|
|
|
|
readonly POSH_THEME_URL="https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/catppuccin.omp.json"
|
|
readonly ZSHRC_URL="https://raw.githubusercontent.com/marcogll/mac_vntySet/main/.zshrc.example"
|
|
readonly POSH_THEME_PATH="$HOME/.poshthemes/catppuccin.omp.json"
|
|
|
|
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
echo "Este instalador solo funciona en macOS." >&2
|
|
exit 1
|
|
fi
|
|
|
|
trap 'echo -e "\nSe produjo un error. Revisa los mensajes anteriores para más detalles." >&2' ERR
|
|
|
|
BREW_BIN=""
|
|
|
|
retry_command() {
|
|
local -r max_attempts=3
|
|
local attempt=1
|
|
local -a cmd=("$@")
|
|
|
|
until "${cmd[@]}"; do
|
|
if (( attempt >= max_attempts )); then
|
|
return 1
|
|
fi
|
|
sleep "$((attempt * 2))"
|
|
attempt=$((attempt + 1))
|
|
done
|
|
return 0
|
|
}
|
|
|
|
readonly GITHUB_CLONE_PREFIXES=(
|
|
"https://github.com/"
|
|
"https://ghproxy.com/https://github.com/"
|
|
"https://hub.fastgit.org/"
|
|
"https://github.com.cnpmjs.org/"
|
|
)
|
|
|
|
readonly ARCHIVE_TEMPLATES=(
|
|
"https://codeload.github.com/%s/tar.gz/HEAD"
|
|
"https://ghproxy.com/https://codeload.github.com/%s/tar.gz/HEAD"
|
|
"https://github.com/%s/archive/HEAD.tar.gz"
|
|
"https://ghproxy.com/https://github.com/%s/archive/HEAD.tar.gz"
|
|
)
|
|
|
|
clone_plugin_repo() {
|
|
local repo="$1"
|
|
local destination="$2"
|
|
|
|
if ! command -v git >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
for prefix in "${GITHUB_CLONE_PREFIXES[@]}"; do
|
|
rm -rf "$destination"
|
|
local url="${prefix}${repo}.git"
|
|
if retry_command git clone "$url" "$destination"; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
download_plugin_archive() {
|
|
local repo="$1"
|
|
local destination="$2"
|
|
|
|
if ! command -v curl >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
rm -rf "$destination"
|
|
for template in "${ARCHIVE_TEMPLATES[@]}"; do
|
|
mkdir -p "$destination"
|
|
local archive_url
|
|
archive_url=$(printf "$template" "$repo")
|
|
if curl -fsSL "$archive_url" | tar -xzf - -C "$destination" --strip-components=1 >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
rm -rf "$destination"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
ensure_brew_shellenv() {
|
|
if command -v brew >/dev/null 2>&1; then
|
|
BREW_BIN=$(command -v brew)
|
|
eval "$("$BREW_BIN" shellenv)"
|
|
return
|
|
fi
|
|
|
|
for candidate in /opt/homebrew/bin/brew /usr/local/bin/brew; do
|
|
if [ -x "$candidate" ]; then
|
|
BREW_BIN="$candidate"
|
|
eval "$("$BREW_BIN" shellenv)"
|
|
return
|
|
fi
|
|
done
|
|
}
|
|
|
|
install_homebrew() {
|
|
ensure_xcode_clt
|
|
|
|
if [ -z "$BREW_BIN" ] && [ ! -x /opt/homebrew/bin/brew ] && [ ! -x /usr/local/bin/brew ]; then
|
|
echo "Instalando Homebrew…"
|
|
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
fi
|
|
|
|
ensure_brew_shellenv
|
|
|
|
if [ -n "$BREW_BIN" ]; then
|
|
local shell_line="eval \"\$($BREW_BIN shellenv)\""
|
|
if ! grep -qsF "$shell_line" "$HOME/.zprofile" 2>/dev/null; then
|
|
printf '%s\n' "$shell_line" >> "$HOME/.zprofile"
|
|
fi
|
|
fi
|
|
|
|
brew update
|
|
}
|
|
|
|
brew_ensure_formula() {
|
|
local formula="$1"
|
|
if brew list --formula "$formula" >/dev/null 2>&1; then
|
|
echo "✔︎ ${formula} ya está instalado. Omitiendo."
|
|
return
|
|
fi
|
|
|
|
echo "➜ Instalando ${formula}…"
|
|
brew install "$formula"
|
|
}
|
|
|
|
brew_ensure_cask() {
|
|
local cask="$1"
|
|
if brew list --cask "$cask" >/dev/null 2>&1; then
|
|
echo "✔︎ ${cask} ya está instalado. Omitiendo."
|
|
return
|
|
fi
|
|
|
|
echo "➜ Instalando ${cask}…"
|
|
local output
|
|
if ! output=$(brew install --cask "$cask" 2>&1); then
|
|
if [[ "$output" == *"already a Font at"* ]]; then
|
|
echo "✔︎ La fuente de ${cask} ya existe. Omitiendo."
|
|
else
|
|
echo "$output" >&2
|
|
echo "Error al instalar ${cask}." >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
install_cli_dependencies() {
|
|
echo "Instalando herramientas base de desarrollo…"
|
|
local formulas=(
|
|
zsh
|
|
curl
|
|
wget
|
|
git
|
|
jq
|
|
yq
|
|
node
|
|
python
|
|
go
|
|
direnv
|
|
yt-dlp
|
|
ffmpeg
|
|
speedtest-cli
|
|
jandedobbeleer/oh-my-posh/oh-my-posh
|
|
)
|
|
|
|
for formula in "${formulas[@]}"; do
|
|
brew_ensure_formula "$formula"
|
|
done
|
|
|
|
echo "Instalando Oh My Posh y fuentes Nerd Font…"
|
|
brew tap homebrew/cask-fonts >/dev/null 2>&1 || true
|
|
brew_ensure_cask font-meslo-lg-nerd-font
|
|
brew_ensure_cask zerotier-one
|
|
}
|
|
|
|
setup_media_dirs() {
|
|
mkdir -p "$HOME/Movies/Youtube" "$HOME/Music/Youtube"
|
|
}
|
|
|
|
configure_terminal_font() {
|
|
if ! command -v osascript >/dev/null 2>&1; then
|
|
return
|
|
fi
|
|
|
|
local font_name font_size
|
|
font_name="MesloLGS Nerd Font"
|
|
font_size=14
|
|
|
|
echo "Configurando Terminal para usar la fuente $font_name…"
|
|
/usr/bin/osascript <<OSA >/dev/null 2>&1
|
|
tell application "Terminal"
|
|
try
|
|
set font name of default settings to "$font_name"
|
|
set font size of default settings to "$font_size"
|
|
set font name of startup settings to "$font_name"
|
|
set font size of startup settings to "$font_size"
|
|
end try
|
|
end tell
|
|
OSA
|
|
}
|
|
|
|
install_oh_my_zsh() {
|
|
echo "Instalando Oh My Zsh y plugins…"
|
|
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
|
RUNZSH=no CHSH=no KEEP_ZSHRC=yes \
|
|
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
|
|
fi
|
|
|
|
mkdir -p "$HOME/.oh-my-zsh/custom/plugins"
|
|
|
|
local repos=(
|
|
"zsh-users/zsh-autosuggestions"
|
|
"zsh-users/zsh-syntax-highlighting"
|
|
"zsh-users/zsh-completions"
|
|
)
|
|
|
|
for repo in "${repos[@]}"; do
|
|
local destination="$HOME/.oh-my-zsh/custom/plugins/$(basename "$repo")"
|
|
if [ -d "$destination/.git" ]; then
|
|
git -C "$destination" pull --ff-only >/dev/null 2>&1 || true
|
|
continue
|
|
fi
|
|
|
|
if clone_plugin_repo "$repo" "$destination"; then
|
|
continue
|
|
fi
|
|
|
|
echo "No se pudo clonar https://github.com/${repo}.git con git, intentando descargar el archivo..." >&2
|
|
if download_plugin_archive "$repo" "$destination"; then
|
|
continue
|
|
fi
|
|
|
|
echo "Advertencia: no se pudo instalar ${repo}. Verifica la conexión e inténtalo de nuevo." >&2
|
|
done
|
|
}
|
|
|
|
install_zsh_config() {
|
|
install_cli_dependencies
|
|
install_oh_my_zsh
|
|
setup_media_dirs
|
|
configure_terminal_font
|
|
|
|
echo "Configurando Oh My Posh…"
|
|
mkdir -p "$HOME/.poshthemes"
|
|
curl -fsSL "$POSH_THEME_URL" -o "$POSH_THEME_PATH"
|
|
|
|
echo "Descargando .zshrc de Vanity Shell…"
|
|
if ! curl -fsSL "$ZSHRC_URL" -o "$HOME/.zshrc"; then
|
|
echo "No se pudo descargar la configuración de ZSH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if command -v pbcopy >/dev/null 2>&1; then
|
|
echo "source ~/.zshrc" | pbcopy
|
|
echo "El comando 'source ~/.zshrc' fue copiado al portapapeles."
|
|
fi
|
|
|
|
echo "Configuración de ZSH instalada correctamente."
|
|
}
|
|
|
|
ensure_docker_daemon() {
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "El CLI de Docker no está disponible." >&2
|
|
return 1
|
|
fi
|
|
|
|
if docker info >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
echo "No se detectó un demonio de Docker en ejecución." >&2
|
|
echo "Asegúrate de que Docker Desktop, OrbStack u otro daemon remoto esté activo." >&2
|
|
return 1
|
|
}
|
|
|
|
wait_for_docker_desktop() {
|
|
local attempts=0
|
|
local max_attempts=40
|
|
local delay=5
|
|
|
|
echo "Esperando a que Docker Desktop termine de iniciar…"
|
|
while (( attempts < max_attempts )); do
|
|
if docker info >/dev/null 2>&1; then
|
|
echo "Docker Desktop está operativo."
|
|
return 0
|
|
fi
|
|
sleep "$delay"
|
|
attempts=$((attempts + 1))
|
|
done
|
|
|
|
echo "Docker Desktop no respondió después de $((max_attempts * delay)) segundos." >&2
|
|
return 1
|
|
}
|
|
|
|
install_docker_stack() {
|
|
echo "Instalando Docker CLI y utilidades…"
|
|
brew install docker docker-buildx docker-compose lazydocker
|
|
brew install --cask docker
|
|
|
|
echo "Abriendo Docker Desktop…"
|
|
open -gj -a Docker || open -a Docker || true
|
|
|
|
if ! wait_for_docker_desktop; then
|
|
echo "Se omitió Portainer porque Docker Desktop no está listo." >&2
|
|
echo "Abre Docker Desktop manualmente y vuelve a ejecutar la opción D." >&2
|
|
return
|
|
fi
|
|
|
|
echo "Desplegando Portainer…"
|
|
docker volume create portainer_data >/dev/null 2>&1 || true
|
|
docker stop portainer >/dev/null 2>&1 || true
|
|
docker rm portainer >/dev/null 2>&1 || true
|
|
|
|
docker run -d \
|
|
-p 8000:8000 -p 9443:9443 \
|
|
--name=portainer \
|
|
--restart=always \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-v portainer_data:/data \
|
|
portainer/portainer-ce:latest >/dev/null
|
|
}
|
|
|
|
ensure_xcode_clt() {
|
|
if xcode-select -p >/dev/null 2>&1; then
|
|
return
|
|
fi
|
|
|
|
echo "Instalando Xcode Command Line Tools…"
|
|
echo "macOS abrirá un diálogo gráfico; acepta la instalación para continuar."
|
|
|
|
if ! /usr/bin/xcode-select --install >/dev/null 2>&1; then
|
|
if xcode-select -p >/dev/null 2>&1; then
|
|
return
|
|
fi
|
|
echo "No se pudo iniciar la instalación automática. Ejecuta 'xcode-select --install' manualmente y vuelve a intentarlo." >&2
|
|
exit 1
|
|
fi
|
|
|
|
wait_for_enter "Una vez que la instalación gráfica termine, presiona Enter para continuar… "
|
|
|
|
local attempts=0
|
|
local max_attempts=60
|
|
while ! xcode-select -p >/dev/null 2>&1; do
|
|
sleep 5
|
|
attempts=$((attempts + 1))
|
|
if (( attempts >= max_attempts )); then
|
|
echo "No se detectó la instalación de las Xcode Command Line Tools tras varios intentos." >&2
|
|
echo "Instálalas manualmente con 'xcode-select --install' y vuelve a ejecutar este script." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "Xcode Command Line Tools instaladas correctamente."
|
|
}
|
|
|
|
wait_for_enter() {
|
|
local prompt="$1"
|
|
|
|
if [ -t 0 ]; then
|
|
read -r -p "$prompt" _
|
|
printf "\n"
|
|
return 0
|
|
fi
|
|
|
|
if [ -r /dev/tty ]; then
|
|
printf "%s" "$prompt" > /dev/tty
|
|
read -r _ < /dev/tty
|
|
printf "\n"
|
|
return 0
|
|
fi
|
|
|
|
printf "%s\n" "$prompt"
|
|
return 1
|
|
}
|
|
|
|
read_menu_choice() {
|
|
local prompt="$1"
|
|
|
|
if [ -t 0 ]; then
|
|
if read -r -p "$prompt" REPLY; then
|
|
printf "\n"
|
|
return 0
|
|
fi
|
|
printf "\n"
|
|
return 1
|
|
fi
|
|
|
|
if [ -r /dev/tty ]; then
|
|
printf "%s" "$prompt" > /dev/tty
|
|
if read -r REPLY < /dev/tty; then
|
|
printf "\n"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
printf "\n"
|
|
return 1
|
|
}
|
|
|
|
main_menu() {
|
|
echo "Selecciona una opción:"
|
|
echo " A) Instalar TODO (recomendado)"
|
|
echo " C) Instalar solo configuración ZSH"
|
|
echo " D) Instalar Docker + Portainer + Lazydocker"
|
|
echo " U) Actualizar componentes instalados"
|
|
echo " Q) Salir"
|
|
echo ""
|
|
local choice=""
|
|
if read_menu_choice "Opción [A/C/D/U/Q]: "; then
|
|
choice="$REPLY"
|
|
else
|
|
echo "No se detecta una entrada interactiva; se seleccionará la opción 'A' por defecto."
|
|
choice="A"
|
|
fi
|
|
echo ""
|
|
case "${choice:-A}" in
|
|
A|a)
|
|
install_homebrew
|
|
install_zsh_config
|
|
install_docker_stack
|
|
;;
|
|
C|c)
|
|
install_homebrew
|
|
install_zsh_config
|
|
;;
|
|
D|d)
|
|
install_homebrew
|
|
install_docker_stack
|
|
;;
|
|
U|u)
|
|
echo "Actualizando la instalación existente…"
|
|
install_homebrew
|
|
install_zsh_config
|
|
install_docker_stack
|
|
;;
|
|
Q|q)
|
|
echo "Saliendo…"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Opción inválida." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
clear
|
|
cat <<'BANNER'
|
|
==============================================
|
|
V A N I T Y S H E L L
|
|
macOS Development Installer
|
|
==============================================
|
|
BANNER
|
|
|
|
echo ""
|
|
main_menu
|
|
echo "=============================================="
|
|
echo " Instalación completada."
|
|
echo "=============================================="
|
|
echo ""
|
|
echo "Para aplicar la configuración ejecuta ahora:"
|
|
echo "source ~/.zshrc"
|
|
echo ""
|
|
wait_for_enter "Presiona Enter una vez hayas ejecutado el comando anterior en tu terminal… "
|
|
echo "Reinicia tu Mac para asegurarte de que todas las herramientas queden listas para el próximo arranque."
|
|
echo "Vanity CLI | 'help' sirve para ver los comandos."
|