mac-setup/install.sh

531 lines
18 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# macOS 快速设置 - 完整安装脚本
# 用法: bash -c "$(curl -fsSL https://raw.githubusercontent.com/Astrian/mac-setup/main/install.sh)"
# 本地: bash ~/.mac-setup/install.sh
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 打印带颜色的消息
print_message() {
echo -e "${2}${1}${NC}"
}
print_message "🍎 macOS 快速设置工具" "$CYAN"
print_message "=====================" "$CYAN"
echo ""
# 检查网络连接
check_network() {
if ! ping -c 1 github.com &> /dev/null; then
print_message "❌ 网络连接失败,请检查网络设置" "$RED"
exit 1
fi
}
# 预先获取 sudo 权限
get_sudo() {
print_message "🔐 需要管理员权限..." "$YELLOW"
sudo -v
# 保持 sudo 权限活跃
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
}
# 配置 sudo Touch ID
setup_sudo_touchid() {
print_message "👆 配置 sudo Touch ID..." "$BLUE"
if grep -q "auth.*sufficient.*pam_tid.so" /etc/pam.d/sudo_local 2>/dev/null; then
print_message "✅ Touch ID 已配置" "$GREEN"
else
sudo sed -i '' 's/^#auth.*sufficient.*pam_tid.so/auth sufficient pam_tid.so/' /etc/pam.d/sudo_local 2>/dev/null || true
print_message "✅ Touch ID 配置完成" "$GREEN"
fi
}
# 安装 Xcode Command Line Tools
install_xcode_tools() {
print_message "🛠️ 检查 Xcode Command Line Tools..." "$BLUE"
if xcode-select -p &>/dev/null; then
print_message "✅ Xcode Command Line Tools 已安装" "$GREEN"
else
print_message "📥 安装 Xcode Command Line Tools..." "$YELLOW"
xcode-select --install
# 检测是否为交互式执行
if [[ -t 0 ]]; then
# 交互式执行 - 等待用户确认
print_message "⏳ Xcode Command Line Tools 安装进行中..." "$YELLOW"
print_message " 1. 请在弹出的对话框中点击 '安装'" "$YELLOW"
print_message " 2. 等待安装完成(通常需要几分钟)" "$YELLOW"
print_message " 3. 安装完成后按回车键继续..." "$YELLOW"
echo ""
# 等待用户确认安装完成
read -p "⚡ 安装完成后请按回车键继续..." -r
echo ""
else
# 非交互式执行 - 自动等待
print_message "⏳ 等待 Xcode Command Line Tools 安装完成..." "$YELLOW"
print_message " 正在自动检测安装状态..." "$YELLOW"
# 自动等待安装完成最多等待10分钟
local wait_count=0
while [[ $wait_count -lt 120 ]]; do
if xcode-select -p &>/dev/null; then
print_message "✅ Xcode Command Line Tools 安装完成!" "$GREEN"
return 0
fi
sleep 5
((wait_count++))
if [[ $((wait_count % 12)) -eq 0 ]]; then
print_message " 仍在等待安装完成... ($((wait_count * 5 / 60))分钟)" "$YELLOW"
fi
done
print_message "❌ Xcode Command Line Tools 安装超时" "$RED"
print_message " 请手动完成安装后重新运行脚本" "$RED"
exit 1
fi
# 再次验证安装是否成功
local retry_count=0
while [[ $retry_count -lt 3 ]]; do
if xcode-select -p &>/dev/null; then
print_message "✅ Xcode Command Line Tools 安装成功!" "$GREEN"
return 0
else
print_message "⚠️ 检测到 Xcode Command Line Tools 尚未安装完成" "$YELLOW"
read -p " 请等待安装完成后按回车键重试... " -r
echo ""
((retry_count++))
fi
done
# 如果多次重试仍未成功,提示用户
print_message "❌ Xcode Command Line Tools 安装验证失败" "$RED"
print_message " 请确保安装已完成或手动验证xcode-select -p" "$RED"
exit 1
fi
}
# 安装 Homebrew
install_homebrew() {
if ! command -v brew &> /dev/null; then
print_message "📦 安装 Homebrew..." "$BLUE"
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 检测系统架构并添加 Homebrew 到 PATH
if [[ $(uname -m) == "arm64" ]]; then
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
else
echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/usr/local/bin/brew shellenv)"
fi
print_message "✅ Homebrew 安装成功!" "$GREEN"
else
print_message "✅ Homebrew 已安装" "$GREEN"
fi
}
# 安装 xcodes
install_xcodes() {
print_message "📱 安装 xcodes (Xcode 版本管理工具)..." "$BLUE"
if command -v xcodes &> /dev/null; then
print_message "✅ xcodes 已安装" "$GREEN"
else
brew install xcodesorg/made/xcodes
brew install aria2
print_message "✅ xcodes 安装成功!" "$GREEN"
fi
}
# 安装应用列表
install_apps() {
print_message "📱 安装应用程序..." "$BLUE"
# 应用程序列表
apps=(
"wechat" # 微信
"qq" # QQ
"discord" # Discord
"setapp" # Setapp
"jordanbaird-ice" # Ice - macOS 菜单栏管理工具
"alfred" # Alfred - 效率启动器
"1password" # 1Password - 密码管理器
"iterm2" # iTerm2 - 终端仿真器
"synology-drive" # Synology Drive Client - 文件同步工具
"thebrowsercompany-dia" # Dia - AI 浏览器
"docker-desktop" # Docker Desktop - 容器管理平台
"gpg-suite" # GPG Suite - PGP 加密工具包
"mailmate" # MailMate - 专业邮件客户端
"mumuplayer" # MuMu Player Pro - Android 模拟器
"maa" # MAA - 明日方舟助手
"tailscale-app" # Tailscale - 零配置 VPN
"private-internet-access" # Private Internet Access - VPN 服务
"parallels" # Parallels Desktop - 虚拟机软件
"telegram" # Telegram Swift - 官方 macOS 版本
"steam" # Steam - 游戏平台
"chiaki" # Chiaki - PlayStation 远程游戏
"moonlight" # Moonlight - NVIDIA GameStream 客户端
"hyperconnect" # HyperConnect - 小米互联服务
"font-fira-code-nerd-font" # Fira Code Nerd Font - 编程字体
"localsend" # LocalSend - 跨平台文件传输工具
"google-chrome@canary" # Google Chrome Canary - Chrome 开发版
"betterdisplay" # BetterDisplay - 显示器管理工具
"netnewswire" # NetNewsWire - RSS 阅读器
"plexamp" # Plexamp - Plex 音乐播放器
"fantastical" # Fantastical - 日历应用
"chatgpt" # ChatGPT - OpenAI 官方应用
"claude" # Claude - Anthropic 官方应用
)
total=${#apps[@]}
installed=0
failed=0
for app in "${apps[@]}"; do
print_message "📦 正在安装: $app" "$YELLOW"
if brew install --cask "$app" 2>/dev/null; then
print_message "$app 安装成功!" "$GREEN"
((installed++))
else
print_message "$app 安装失败或已存在" "$RED"
((failed++))
fi
done
print_message "=========================================" "$CYAN"
print_message "📊 应用安装完成!" "$CYAN"
print_message " 总计: $total 个应用" "$CYAN"
print_message " 成功: $installed" "$GREEN"
print_message " 失败/已存在: $failed" "$RED"
print_message "=========================================" "$CYAN"
}
# 安装命令行工具
install_cli_tools() {
print_message "🛠️ 安装命令行工具..." "$BLUE"
# Neovim
if command -v nvim &> /dev/null; then
print_message "✅ Neovim 已安装" "$GREEN"
else
brew install neovim
print_message "✅ Neovim 安装成功!" "$GREEN"
fi
# prettyping
if command -v prettyping &> /dev/null; then
print_message "✅ prettyping 已安装" "$GREEN"
else
brew install prettyping
print_message "✅ prettyping 安装成功!" "$GREEN"
fi
}
# 配置 SSH
setup_ssh() {
print_message "🔐 配置 SSH..." "$BLUE"
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 配置 1Password SSH Agent
if ! grep -q "IdentityAgent" ~/.ssh/config 2>/dev/null; then
print_message " 配置 1Password SSH Agent..." "$YELLOW"
cat >> ~/.ssh/config << 'EOF'
Host *
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
EOF
chmod 600 ~/.ssh/config
print_message "✅ SSH Agent 配置完成" "$GREEN"
else
print_message "✅ SSH Agent 已配置" "$GREEN"
fi
# 添加常用 Git 服务器指纹
print_message " 添加 Git 服务器指纹..." "$YELLOW"
ssh-keyscan -H git.nas.astrian.moe >> ~/.ssh/known_hosts 2>/dev/null
ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2>/dev/null
ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts 2>/dev/null
}
# 安装 Oh My Zsh
install_oh_my_zsh() {
print_message "🌈 安装 Oh My Zsh..." "$PURPLE"
if [ -d "$HOME/.oh-my-zsh" ]; then
print_message "✅ Oh My Zsh 已安装" "$GREEN"
else
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
print_message "✅ Oh My Zsh 安装成功!" "$GREEN"
fi
}
# 安装 nvm 和 Node.js
install_node() {
print_message "🔧 安装 nvm 和 Node.js..." "$BLUE"
if [ -s "$HOME/.nvm/nvm.sh" ]; then
print_message "✅ nvm 已安装" "$GREEN"
else
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
print_message "✅ nvm 安装成功!" "$GREEN"
fi
# 确保 nvm 已加载
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
print_message "📦 安装 Node.js LTS..." "$YELLOW"
nvm install --lts
nvm use --lts
nvm alias default lts/*
print_message "✅ Node.js LTS 安装成功!" "$GREEN"
}
# 安装 Claude Code CLI
install_claude_code() {
print_message "🤖 安装 Claude Code CLI..." "$BLUE"
npm install -g @anthropic-ai/claude-code
print_message "✅ Claude Code CLI 安装成功!" "$GREEN"
}
# 配置 zsh 别名
setup_aliases() {
print_message "⚙️ 配置 zsh 别名..." "$BLUE"
# Claude Code 别名
if ! grep -q 'alias cc=' "$HOME/.zshrc" 2>/dev/null; then
echo '' >> "$HOME/.zshrc"
echo '# Claude Code CLI alias' >> "$HOME/.zshrc"
echo 'alias cc="claude --dangerously-skip-permissions"' >> "$HOME/.zshrc"
fi
# vi/vim 别名
if ! grep -q 'alias vi=' "$HOME/.zshrc" 2>/dev/null; then
echo '# Neovim alias' >> "$HOME/.zshrc"
echo 'alias vi="nvim"' >> "$HOME/.zshrc"
echo 'alias vim="nvim"' >> "$HOME/.zshrc"
fi
# ping 别名
if ! grep -q 'alias ping=' "$HOME/.zshrc" 2>/dev/null; then
echo '# prettyping alias' >> "$HOME/.zshrc"
echo 'alias ping="prettyping --nolegend"' >> "$HOME/.zshrc"
fi
print_message "✅ 别名配置完成" "$GREEN"
}
# 提示配置 1Password SSH Agent
prompt_1password_setup() {
print_message "🔐 配置 1Password SSH Agent..." "$BLUE"
# 检查 1Password 是否已安装
if [[ -d "/Applications/1Password 7 - Password Manager.app" ]] || [[ -d "/Applications/1Password.app" ]]; then
print_message "📱 检测到 1Password 已安装" "$GREEN"
# 检测是否为交互式执行
if [[ -t 0 ]]; then
# 交互式执行 - 提示用户配置
print_message "⚠️ 为了克隆 nvim 配置,需要先设置 1Password SSH Agent" "$YELLOW"
print_message "" "$NC"
print_message "📋 请按以下步骤配置:" "$YELLOW"
print_message " 1. 打开 1Password 应用并登录" "$YELLOW"
print_message " 2. 进入 Settings > Developer" "$YELLOW"
print_message " 3. 启用 'Use the SSH agent'" "$YELLOW"
print_message " 4. 将你的 SSH 私钥添加到 1Password" "$YELLOW"
print_message " 5. 确保 SSH 密钥可以访问 git.nas.astrian.moe" "$YELLOW"
print_message "" "$NC"
# 等待用户确认配置完成
read -p "⚡ 1Password SSH Agent 配置完成后请按回车键继续..." -r
echo ""
# 测试 SSH 连接
print_message "🔍 测试 SSH 连接..." "$BLUE"
if ssh -T git@git.nas.astrian.moe -o ConnectTimeout=10 -o BatchMode=yes 2>/dev/null; then
print_message "✅ SSH 连接测试成功!" "$GREEN"
return 0
else
print_message "⚠️ SSH 连接测试失败,但将继续执行" "$YELLOW"
print_message " 如果 nvim 配置克隆失败,请检查 SSH 配置" "$YELLOW"
fi
else
# 非交互式执行 - 自动尝试
print_message "🔍 自动检测 SSH 连接状态..." "$YELLOW"
if ssh -T git@git.nas.astrian.moe -o ConnectTimeout=10 -o BatchMode=yes 2>/dev/null; then
print_message "✅ SSH 连接可用" "$GREEN"
else
print_message "⚠️ SSH 连接不可用nvim 配置可能无法克隆" "$YELLOW"
fi
fi
else
print_message "⚠️ 未检测到 1Password跳过 SSH Agent 配置" "$YELLOW"
fi
}
# 配置 Neovim
setup_neovim() {
print_message "⚙️ 配置 Neovim..." "$BLUE"
if command -v nvim &> /dev/null; then
mkdir -p ~/.config
if [ -d ~/.config/nvim ]; then
print_message "⚠️ 备份现有 nvim 配置..." "$YELLOW"
mv ~/.config/nvim ~/.config/nvim.backup.$(date +%Y%m%d_%H%M%S)
fi
print_message "📥 克隆 nvim 配置..." "$YELLOW"
if git clone git@git.nas.astrian.moe:Astrian/nvim-config.git ~/.config/nvim 2>/dev/null; then
print_message "✅ Neovim 配置已应用!" "$GREEN"
else
print_message "⚠️ 无法克隆 nvim 配置" "$YELLOW"
print_message " 请确保已正确配置 1Password SSH Agent" "$YELLOW"
print_message " 或手动执行git clone git@git.nas.astrian.moe:Astrian/nvim-config.git ~/.config/nvim" "$YELLOW"
fi
fi
}
# 显示菜单
show_menu() {
echo ""
print_message "请选择安装选项:" "$CYAN"
echo "1) 完整安装(推荐)"
echo "2) 仅安装 Homebrew 和应用"
echo "3) 仅配置开发环境"
echo "4) 仅配置 SSH 和别名"
echo "0) 退出"
echo ""
read -p "请输入选项 [0-4]: " choice
}
# 完整安装
full_install() {
print_message "🚀 开始完整安装..." "$CYAN"
echo ""
check_network
get_sudo
setup_sudo_touchid
install_xcode_tools
install_homebrew
install_xcodes
install_apps
install_cli_tools
setup_ssh
install_oh_my_zsh
install_node
install_claude_code
setup_aliases
prompt_1password_setup
setup_neovim
echo ""
print_message "=========================================" "$CYAN"
print_message "🎉 完整安装完成!" "$GREEN"
print_message "=========================================" "$CYAN"
echo ""
print_message "💡 提示:" "$YELLOW"
print_message " 1. 重启终端或运行 'source ~/.zshrc' 激活配置" "$YELLOW"
print_message " 2. 使用 'cc' 命令启动 Claude Code" "$YELLOW"
print_message " 3. 使用 'vi' 命令启动 Neovim" "$YELLOW"
print_message " 4. 首次启动 nvim 时会自动安装插件" "$YELLOW"
}
# 仅安装应用
apps_only() {
print_message "📱 仅安装应用..." "$CYAN"
check_network
get_sudo
install_xcode_tools
install_homebrew
install_xcodes
install_apps
print_message "✅ 应用安装完成!" "$GREEN"
}
# 仅开发环境
dev_only() {
print_message "🛠️ 仅配置开发环境..." "$CYAN"
install_cli_tools
install_oh_my_zsh
install_node
install_claude_code
prompt_1password_setup
setup_neovim
print_message "✅ 开发环境配置完成!" "$GREEN"
}
# 仅配置
config_only() {
print_message "⚙️ 仅配置 SSH 和别名..." "$CYAN"
setup_ssh
setup_aliases
print_message "✅ 配置完成!" "$GREEN"
}
# 主执行逻辑
main() {
# 如果有参数,直接运行完整安装
if [[ $# -gt 0 && $1 == "--full" ]]; then
full_install
exit 0
fi
# 如果是通过 curl 管道执行(非交互式),默认运行完整安装
if [[ ! -t 0 ]]; then
print_message "🔍 检测到非交互式执行,运行完整安装..." "$YELLOW"
full_install
exit 0
fi
# 交互式菜单
while true; do
show_menu
case $choice in
1)
full_install
break
;;
2)
apps_only
break
;;
3)
dev_only
break
;;
4)
config_only
break
;;
0)
print_message "👋 再见!" "$CYAN"
exit 0
;;
*)
print_message "❌ 无效选项,请重试" "$RED"
;;
esac
done
}
# 只有在脚本被直接执行时才运行主函数
if [[ "${BASH_SOURCE[0]}" == "${0}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
main "$@"
fi