Private
Public Access
1
0
Files
xcodecli-shells/setup.sh

615 lines
16 KiB
Bash
Executable File
Raw 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
# XCodeCLI Setup Launcher (macOS / Linux)
# 一站式安装和配置 Claude Code, Gemini CLI, Codex
set -e
# 确保 PATH 包含常见的包管理器路径
export PATH="$HOME/.local/share/fnm:$HOME/.fnm:$HOME/.local/bin:$HOME/.npm-global/bin:/usr/local/bin:$PATH"
# ========== 颜色定义 ==========
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# ========== 输出函数 ==========
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ========== 检测 shell 配置文件 ==========
get_shell_rc() {
if [ -n "${ZSH_VERSION:-}" ] || [ "${SHELL##*/}" = "zsh" ]; then
echo "$HOME/.zshrc"
elif [ -n "${BASH_VERSION:-}" ] || [ "${SHELL##*/}" = "bash" ]; then
if [ -f "$HOME/.bashrc" ]; then
echo "$HOME/.bashrc"
else
echo "$HOME/.bash_profile"
fi
else
error "不支持当前 shell: ${SHELL##*/}"
error "仅支持 bash 和 zsh"
exit 1
fi
}
# ========== 刷新环境变量 ==========
reload_env() {
local rc_file
rc_file=$(get_shell_rc)
# 重新加载 PATH
export PATH="$HOME/.local/share/fnm:$HOME/.fnm:$HOME/.local/bin:$HOME/.npm-global/bin:/usr/local/bin:$PATH"
# 加载 fnm 环境(如果存在)
if command_exists fnm; then
eval "$(fnm env)" 2>/dev/null || true
fi
# 从配置文件中提取并加载环境变量
if [ -f "$rc_file" ]; then
while IFS= read -r line; do
if [[ "$line" =~ ^export\ ([A-Za-z_][A-Za-z0-9_]*)= ]]; then
eval "$line" 2>/dev/null || true
fi
done < "$rc_file"
fi
# 刷新命令缓存
hash -r 2>/dev/null || true
}
# ========== 工具配置(兼容 bash 3.x==========
TOOL_NAMES_1="Claude Code"
TOOL_NAMES_2="Gemini CLI"
TOOL_NAMES_3="Codex"
TOOL_CMDS_1="claude"
TOOL_CMDS_2="gemini"
TOOL_CMDS_3="codex"
TOOL_PKGS_1="@anthropic-ai/claude-code"
TOOL_PKGS_2="@google/gemini-cli@latest"
TOOL_PKGS_3="@openai/codex"
TOOL_URLS_1="https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/ClaudeCode/setup-claude-code.sh"
TOOL_URLS_2="https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/GeminiCLI/setup-gemini.sh"
TOOL_URLS_3="https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/codex/setup-codex.sh"
# Helper functions to get tool properties
get_tool_name() { eval echo \"\$TOOL_NAMES_$1\"; }
get_tool_cmd() { eval echo \"\$TOOL_CMDS_$1\"; }
get_tool_pkg() { eval echo \"\$TOOL_PKGS_$1\"; }
get_tool_url() { eval echo \"\$TOOL_URLS_$1\"; }
# ========== 检测系统包管理器 ==========
get_sys_pkg_manager() {
if command_exists apt-get; then
echo "apt"
elif command_exists yum; then
echo "yum"
elif command_exists dnf; then
echo "dnf"
elif command_exists apk; then
echo "apk"
elif command_exists brew; then
echo "brew"
elif command_exists pacman; then
echo "pacman"
else
echo ""
fi
}
# ========== 安装系统依赖 ==========
install_sys_deps() {
local deps=("$@")
local pkg_mgr=$(get_sys_pkg_manager)
if [[ -z "$pkg_mgr" ]]; then
error "无法检测系统包管理器,请手动安装: ${deps[*]}"
return 1
fi
info "正在安装: ${deps[*]} ..."
case "$pkg_mgr" in
apt)
apt-get update -qq && apt-get install -y "${deps[@]}"
;;
yum)
yum install -y "${deps[@]}"
;;
dnf)
dnf install -y "${deps[@]}"
;;
apk)
apk add "${deps[@]}"
;;
brew)
brew install "${deps[@]}"
;;
pacman)
pacman -S --noconfirm "${deps[@]}"
;;
esac
# 验证所有依赖是否安装成功
local failed=()
for dep in "${deps[@]}"; do
if command_exists "$dep"; then
success "$dep 安装成功!"
else
failed+=("$dep")
fi
done
if [[ ${#failed[@]} -gt 0 ]]; then
error "以下依赖安装失败: ${failed[*]}"
return 1
fi
return 0
}
# ========== 检测并安装系统依赖 ==========
check_system_deps() {
local missing_deps=()
# 检测 curl
if ! command_exists curl; then
missing_deps+=("curl")
fi
# 检测 unzip (fnm 安装需要)
if ! command_exists unzip; then
missing_deps+=("unzip")
fi
# 检测 jq (Claude Code 配置需要)
if ! command_exists jq; then
missing_deps+=("jq")
fi
if [[ ${#missing_deps[@]} -eq 0 ]]; then
return 0
fi
echo ""
warning "检测到缺少以下系统依赖: ${missing_deps[*]}"
echo ""
read -p "是否自动安装这些依赖? (Y/n) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Nn]$ ]]; then
error "请手动安装依赖后重新运行脚本"
echo ""
local pkg_mgr=$(get_sys_pkg_manager)
case "$pkg_mgr" in
apt)
echo -e " ${CYAN}apt-get update && apt-get install -y ${missing_deps[*]}${NC}"
;;
yum)
echo -e " ${CYAN}yum install -y ${missing_deps[*]}${NC}"
;;
dnf)
echo -e " ${CYAN}dnf install -y ${missing_deps[*]}${NC}"
;;
apk)
echo -e " ${CYAN}apk add ${missing_deps[*]}${NC}"
;;
brew)
echo -e " ${CYAN}brew install ${missing_deps[*]}${NC}"
;;
pacman)
echo -e " ${CYAN}pacman -S ${missing_deps[*]}${NC}"
;;
*)
echo -e " 请使用系统包管理器安装: ${missing_deps[*]}"
;;
esac
echo ""
return 1
fi
# 一次性安装所有缺失的依赖
if ! install_sys_deps "${missing_deps[@]}"; then
return 1
fi
return 0
}
# ========== 帮助信息 ==========
show_help() {
cat << 'EOF'
XCodeCLI Setup Launcher (macOS / Linux)
一站式安装和配置 Claude Code, Gemini CLI, Codex
Usage: ./setup.sh [OPTIONS]
Options:
--key <KEY> 预设 API 密钥(必填)
--help 显示此帮助信息
一行命令快速使用:
API_KEY='YOUR_API_KEY' bash -c "$(curl -fsSL https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/setup.sh)"
EOF
exit 0
}
# ========== 命令检测 ==========
command_exists() {
command -v "$1" &> /dev/null
}
# ========== Node.js 版本检测 ==========
get_node_version() {
if command_exists node; then
node --version 2>/dev/null | sed 's/v//'
fi
}
get_node_major_version() {
local version
version=$(get_node_version)
if [ -n "$version" ]; then
echo "$version" | cut -d. -f1
fi
}
# ========== 安装 fnm ==========
install_fnm() {
echo ""
info "正在安装 fnm (Fast Node Manager)..."
if curl -fsSL https://fnm.vercel.app/install | bash; then
export PATH="$HOME/.local/share/fnm:$PATH"
if [ -f "$HOME/.local/share/fnm/fnm" ]; then
eval "$($HOME/.local/share/fnm/fnm env)" 2>/dev/null || true
fi
if command_exists fnm; then
success "fnm 安装成功!"
return 0
else
warning "fnm 可能已安装,但需要重新打开终端才能生效"
info "请重新打开终端后再运行此脚本"
return 1
fi
else
error "fnm 安装失败"
return 1
fi
}
# ========== 使用 fnm 安装 Node.js ==========
install_node_with_fnm() {
info "使用 fnm 安装 Node.js 24.x..."
if fnm install 24 && fnm use 24 && fnm default 24; then
eval "$(fnm env)" 2>/dev/null || true
if command_exists node; then
local version
version=$(get_node_version)
success "Node.js v$version 安装成功!"
return 0
else
warning "Node.js 可能已安装,但需要重新打开终端才能生效"
return 1
fi
else
error "Node.js 安装失败"
return 1
fi
}
# ========== 确保 Node.js 环境就绪 ==========
ensure_node_environment() {
local version major
version=$(get_node_version)
if [ -n "$version" ]; then
major=$(get_node_major_version)
info "检测到 Node.js v$version"
if [ "$major" -lt 20 ]; then
warning "Node.js 版本过低 (需要 >= 20.x)"
echo ""
read -p "是否使用 fnm 安装 Node.js 24.x? (Y/n) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Nn]$ ]]; then
error "Node.js 版本不满足要求,请手动升级后重试"
return 1
fi
if ! command_exists fnm; then
install_fnm || return 1
fi
install_node_with_fnm || return 1
fi
return 0
fi
warning "未检测到 Node.js"
info "将使用 fnm 安装 Node.js 24.x"
echo ""
read -p "是否继续? (Y/n) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Nn]$ ]]; then
return 1
fi
if ! command_exists fnm; then
install_fnm || return 1
fi
install_node_with_fnm || return 1
}
# ========== 安装工具 ==========
install_tool() {
local tool_num=$1
local tool_name=$(get_tool_name "$tool_num")
local tool_pkg=$(get_tool_pkg "$tool_num")
info "安装 $tool_name..."
local install_cmd="npm install -g $tool_pkg"
echo -e " 执行: ${CYAN}$install_cmd${NC}"
if eval "$install_cmd"; then
hash -r 2>/dev/null || true
success "$tool_name 安装成功!"
return 0
else
error "$tool_name 安装失败"
return 1
fi
}
# ========== 配置工具 ==========
configure_tool() {
local tool_num=$1
local api_key=$2
local tool_name=$(get_tool_name "$tool_num")
local setup_url=$(get_tool_url "$tool_num")
info "配置 $tool_name..."
if API_KEY="$api_key" bash -c "$(curl -fsSL "$setup_url")"; then
success "$tool_name 配置完成!"
return 0
else
error "$tool_name 配置失败"
return 1
fi
}
# ========== 显示状态 ==========
show_status() {
echo ""
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} XCodeCLI Setup Launcher${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
echo -e "获取 API 密钥: ${CYAN}https://api2.xcodecli.com/console/token${NC}"
echo -e "步骤: 添加令牌 → 输入令牌名称 → 提交"
echo ""
# 显示 Node.js 状态
local version major
version=$(get_node_version)
if [ -n "$version" ]; then
major=$(get_node_major_version)
local status color
if [ "$major" -ge 20 ]; then
status="[OK]"
color="${GREEN}"
else
status="[需升级]"
color="${YELLOW}"
fi
echo -e " Node.js: v$version ${color}${status}${NC}"
else
echo -e " Node.js: ${RED}[未安装]${NC}"
fi
echo ""
echo -e "${YELLOW}工具安装状态:${NC}"
echo ""
for i in 1 2 3; do
local tool_name=$(get_tool_name "$i")
local tool_cmd=$(get_tool_cmd "$i")
local status
local color
if command_exists "$tool_cmd"; then
status="[已安装]"
color="${GREEN}"
else
status="[未安装]"
color="${RED}"
fi
printf " [%d] %-12s %b%s%b\n" "$i" "$tool_name" "$color" "$status" "$NC"
done
echo ""
echo -e " [${CYAN}a${NC}] 配置全部工具"
echo -e " [${CYAN}0${NC}] 退出"
echo ""
}
# ========== 主函数 ==========
main() {
local api_key="${API_KEY:-}"
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
--key)
api_key="$2"
shift 2
;;
--help|-h)
show_help
;;
*)
shift
;;
esac
done
# 检测并安装系统依赖 (curl, unzip, jq)
if ! check_system_deps; then
exit 1
fi
# 显示状态
show_status
# 选择要配置的工具
echo -e "${YELLOW}请选择要配置的工具 (1/2/3/a/0):${NC}"
read -r choice
# 退出
if [[ "$choice" == "0" ]]; then
info "再见!"
exit 0
fi
# 验证选择
local tools_to_configure=""
if [[ "$choice" == "a" || "$choice" == "A" ]]; then
tools_to_configure="1 2 3"
elif [[ "$choice" =~ ^[1-3]$ ]]; then
tools_to_configure="$choice"
else
error "无效的选择"
exit 1
fi
# 检查 API Key
if [[ -z "$api_key" ]]; then
echo ""
echo -e "${YELLOW}请输入 API 密钥:${NC}"
read -r api_key
if [[ -z "$api_key" ]]; then
error "API 密钥不能为空"
exit 1
fi
fi
info "API 密钥: ${api_key:0:8}..."
echo ""
# 确保 Node.js 环境就绪
if ! ensure_node_environment; then
exit 1
fi
echo ""
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} 开始安装和配置工具${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
local failed=0
local total=$(echo $tools_to_configure | wc -w | tr -d ' ')
local current=0
# 安装和配置选中的工具
for i in $tools_to_configure; do
current=$((current + 1))
local tool_name=$(get_tool_name "$i")
local tool_cmd=$(get_tool_cmd "$i")
echo ""
echo -e "${YELLOW}>>> [$current/$total] $tool_name${NC}"
echo ""
# 检测是否已安装
if ! command_exists "$tool_cmd"; then
if ! install_tool "$i"; then
failed=$((failed + 1))
continue
fi
else
info "$tool_name 已安装,跳过安装步骤"
fi
# 配置工具
if ! configure_tool "$i" "$api_key"; then
failed=$((failed + 1))
fi
done
# 显示结果
echo ""
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} 配置完成${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
# 自动刷新环境变量
info "正在刷新环境变量..."
reload_env
if [[ $failed -eq 0 ]]; then
success "所有选中的工具配置成功!"
echo ""
# 验证工具是否可用
local all_ok=true
for i in $tools_to_configure; do
local tool_cmd=$(get_tool_cmd "$i")
local tool_name=$(get_tool_name "$i")
if command_exists "$tool_cmd"; then
echo -e " ${GREEN}${NC} $tool_name ($tool_cmd) 可用"
else
echo -e " ${RED}${NC} $tool_name ($tool_cmd) 未找到"
all_ok=false
fi
done
echo ""
if [[ "$all_ok" == "false" ]]; then
warning "部分工具在当前会话中不可用"
echo -e "${YELLOW}请执行以下命令刷新环境:${NC}"
echo ""
local rc_file=$(get_shell_rc)
echo -e " ${CYAN}source $rc_file${NC}"
echo ""
echo -e "或者重新打开终端"
else
success "所有工具已就绪,可以直接使用!"
fi
else
warning "$failed 个工具配置失败"
fi
# 询问是否启动新 shell 以使环境变量生效
echo ""
echo -e "${YELLOW}是否启动新的 shell 会话以使环境变量立即生效? (Y/n)${NC}"
read -r -n 1 start_new_shell
echo ""
if [[ ! "$start_new_shell" =~ ^[Nn]$ ]]; then
info "正在启动新的 shell..."
exec "$SHELL" -l
fi
}
main "$@"