forked from Gitlink/gitlink-cli
436 lines
13 KiB
Bash
Executable File
436 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
||
# GitLink CLI 一键安装脚本(改进版)
|
||
# 自动检测平台,下载预编译二进制,安装 skills
|
||
# 用法: curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash
|
||
# 支持: VERSION=0.1.0 INSTALL_DIR=$HOME/.local/bin bash install.sh
|
||
|
||
set -e
|
||
|
||
REPO_OWNER="Gitlink"
|
||
REPO_NAME="gitlink-cli"
|
||
BINARY_NAME="gitlink-cli"
|
||
API_BASE="https://www.gitlink.org.cn"
|
||
INSTALL_DIR="${INSTALL_DIR:-auto}"
|
||
SKILLS_DIR="${HOME}/.gitlink/skills"
|
||
VERSION="${VERSION:-latest}"
|
||
MAX_ATTEMPTS=3
|
||
DEBUG="${DEBUG:-false}"
|
||
|
||
# ---------- 颜色输出 ----------
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||
error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
||
step() { echo -e "${CYAN}[STEP]${NC} $*"; }
|
||
debug() { [[ "${DEBUG}" == "true" ]] && echo -e "${BLUE}[DEBUG]${NC} $*"; }
|
||
|
||
# ---------- 环境检测 ----------
|
||
check_environment() {
|
||
step "检查安装环境..."
|
||
|
||
# 检查必需命令
|
||
local required_commands=("curl" "tar")
|
||
for cmd in "${required_commands[@]}"; do
|
||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||
error "缺少必需命令: $cmd"
|
||
error "请安装后再试:"
|
||
error " Ubuntu/Debian: sudo apt-get install $cmd"
|
||
error " CentOS/RHEL: sudo yum install $cmd"
|
||
error " macOS: brew install $cmd"
|
||
exit 1
|
||
fi
|
||
done
|
||
debug "必需命令检查通过"
|
||
|
||
# 检查磁盘空间(至少50MB)
|
||
local available_space
|
||
available_space=$(df -m "$HOME" | tail -1 | awk '{print $4}')
|
||
if [ "$available_space" -lt 50 ]; then
|
||
error "磁盘空间不足(需要至少50MB,可用: ${available_space}MB)"
|
||
exit 1
|
||
fi
|
||
debug "磁盘空间检查通过: ${available_space}MB"
|
||
|
||
# 检查网络连接
|
||
if ! curl -sSL --connect-timeout 5 "${API_BASE}" >/dev/null 2>&1; then
|
||
error "无法连接到 GitLink 服务器: ${API_BASE}"
|
||
error "请检查网络连接"
|
||
exit 1
|
||
fi
|
||
debug "网络连接检查通过"
|
||
|
||
info "环境检查通过"
|
||
}
|
||
|
||
# ---------- 智能权限处理 ----------
|
||
select_install_dir() {
|
||
local dirs=("$HOME/.local/bin" "$HOME/bin" "/usr/local/bin")
|
||
local selected_dir=""
|
||
|
||
if [ "${INSTALL_DIR}" != "auto" ]; then
|
||
echo "${INSTALL_DIR}"
|
||
return
|
||
fi
|
||
|
||
step "选择安装目录..."
|
||
|
||
# 优先级:用户目录 > 系统目录
|
||
for dir in "${dirs[@]}"; do
|
||
if [ -w "$dir" ] 2>/dev/null || mkdir -p "$dir" 2>/dev/null; then
|
||
selected_dir="$dir"
|
||
info "选择用户目录: $selected_dir"
|
||
break
|
||
fi
|
||
done
|
||
|
||
# 如果用户目录都不可写,尝试系统目录
|
||
if [ -z "$selected_dir" ]; then
|
||
warn "无法写入用户目录,将使用系统目录(需要sudo权限)"
|
||
selected_dir="/usr/local/bin"
|
||
fi
|
||
|
||
echo "$selected_dir"
|
||
}
|
||
|
||
# ---------- 下载重试机制 ----------
|
||
download_with_retry() {
|
||
local url="$1"
|
||
local output="$2"
|
||
local attempt=1
|
||
|
||
while [ $attempt -le $MAX_ATTEMPTS ]; do
|
||
step "下载 (尝试 $attempt/$MAX_ATTEMPTS): $(basename "$url")"
|
||
|
||
if curl -sSL --connect-timeout 10 --max-time 120 --progress-bar "$url" -o "$output" 2>&1; then
|
||
if [ -s "$output" ]; then
|
||
info "下载成功: $(basename "$output") ($(du -h "$output" | cut -f1))"
|
||
return 0
|
||
else
|
||
warn "下载文件为空"
|
||
fi
|
||
else
|
||
warn "下载失败"
|
||
fi
|
||
|
||
if [ $attempt -lt $MAX_ATTEMPTS ]; then
|
||
local wait_time=$((attempt * 2))
|
||
warn "等待 ${wait_time}s 后重试..."
|
||
sleep $wait_time
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
done
|
||
|
||
error "下载失败,已尝试 $MAX_ATTEMPTS 次"
|
||
error "URL: $url"
|
||
error "请检查网络连接或手动下载"
|
||
return 1
|
||
}
|
||
|
||
# ---------- 平台检测 ----------
|
||
detect_platform() {
|
||
local os arch
|
||
case "$(uname -s)" in
|
||
Linux) os="linux" ;;
|
||
Darwin) os="darwin" ;;
|
||
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
|
||
*) error "不支持的操作系统: $(uname -s)"; exit 1 ;;
|
||
esac
|
||
case "$(uname -m)" in
|
||
x86_64|amd64) arch="amd64" ;;
|
||
aarch64|arm64) arch="arm64" ;;
|
||
*) error "不支持的架构: $(uname -m)"; exit 1 ;;
|
||
esac
|
||
echo "${os}/${arch}"
|
||
}
|
||
|
||
# ---------- 获取最新版本 ----------
|
||
fetch_latest_version() {
|
||
local releases_url="${API_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases.json"
|
||
|
||
info "获取最新版本..."
|
||
|
||
local releases
|
||
releases=$(curl -sSL --connect-timeout 10 --max-time 30 "${releases_url}" 2>/dev/null || true)
|
||
|
||
if [ -z "$releases" ]; then
|
||
error "无法获取发布列表: ${releases_url}"
|
||
exit 1
|
||
fi
|
||
|
||
# 尝试解析第一个 release 的 tag_name
|
||
local tag
|
||
tag=$(echo "$releases" | grep -o '"tag_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
||
|
||
if [ -z "$tag" ]; then
|
||
# 备选:尝试直接匹配数组第一个元素的 tag_name
|
||
tag=$(echo "$releases" | grep -oP '"tag_name"\s*:\s*"\K[^"]+' | head -1)
|
||
fi
|
||
|
||
echo "${tag:-v0.1.0}"
|
||
}
|
||
|
||
# ---------- 显示已安装版本 ----------
|
||
show_installed_version() {
|
||
if command -v "${BINARY_NAME}" >/dev/null 2>&1; then
|
||
"${BINARY_NAME}" version 2>/dev/null || echo "未知版本"
|
||
elif [ -x "${INSTALL_DIR}/${BINARY_NAME}" ]; then
|
||
"${INSTALL_DIR}/${BINARY_NAME}" version 2>/dev/null || echo "未知版本"
|
||
else
|
||
echo "未安装"
|
||
fi
|
||
}
|
||
|
||
# ---------- 列出可用版本 ----------
|
||
list_versions() {
|
||
step "查询可用版本..."
|
||
local releases_url="${API_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases.json"
|
||
curl -sSL "$releases_url" | grep -o '"tag_name"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)"$/\1/' | head -10
|
||
}
|
||
|
||
# ---------- 回滚功能 ----------
|
||
rollback_version() {
|
||
local target_version="$1"
|
||
|
||
if [ -z "$target_version" ]; then
|
||
error "请指定要回滚到的版本"
|
||
exit 1
|
||
fi
|
||
|
||
step "回滚到版本: ${target_version}"
|
||
|
||
# 重新安装指定版本
|
||
VERSION="$target_version"
|
||
main
|
||
}
|
||
|
||
# ---------- 卸载功能 ----------
|
||
uninstall() {
|
||
step "卸载 ${BINARY_NAME}..."
|
||
|
||
# 删除二进制
|
||
if [ -f "${INSTALL_DIR}/${BINARY_NAME}" ]; then
|
||
if [ -w "${INSTALL_DIR}" ]; then
|
||
rm -f "${INSTALL_DIR}/${BINARY_NAME}"
|
||
info "已删除: ${INSTALL_DIR}/${BINARY_NAME}"
|
||
else
|
||
sudo rm -f "${INSTALL_DIR}/${BINARY_NAME}"
|
||
info "已删除: ${INSTALL_DIR}/${BINARY_NAME} (使用sudo)"
|
||
fi
|
||
fi
|
||
|
||
# 删除skills
|
||
if [ -d "${SKILLS_DIR}" ]; then
|
||
rm -rf "${SKILLS_DIR}"
|
||
info "已删除: ${SKILLS_DIR}"
|
||
fi
|
||
|
||
# 删除配置(可选)
|
||
echo -n "是否删除配置文件? [y/N] "
|
||
read -r response
|
||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||
rm -rf "$HOME/.config/gitlink-cli"
|
||
rm -rf "$HOME/.gitlink"
|
||
info "已删除配置文件"
|
||
fi
|
||
|
||
info "卸载完成"
|
||
exit 0
|
||
}
|
||
|
||
# ---------- 下载二进制 ----------
|
||
download_binary() {
|
||
local platform="$1"
|
||
local version="$2"
|
||
local os="${platform%/*}"
|
||
local arch="${platform#*/}"
|
||
local archive_name="${BINARY_NAME}_${version}_${os}_${arch}.tar.gz"
|
||
local download_url="${API_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases/${version}/assets/${archive_name}"
|
||
|
||
info "下载: ${archive_name}"
|
||
debug "URL: ${download_url}"
|
||
|
||
local tmpdir
|
||
tmpdir="$(mktemp -d)"
|
||
trap "rm -rf ${tmpdir}" EXIT
|
||
|
||
if ! download_with_retry "${download_url}" "${tmpdir}/${archive_name}"; then
|
||
error "请确认以下版本已发布: ${version}"
|
||
error "发布页: ${API_BASE}/${REPO_OWNER}/${REPO_NAME}/releases"
|
||
exit 1
|
||
fi
|
||
|
||
step "解压..."
|
||
tar -xzf "${tmpdir}/${archive_name}" -C "${tmpdir}"
|
||
|
||
local binary_path="${tmpdir}/${BINARY_NAME}"
|
||
if [ ! -f "${binary_path}" ]; then
|
||
# 可能在子目录中
|
||
binary_path=$(find "${tmpdir}" -name "${BINARY_NAME}" -type f 2>/dev/null | head -1)
|
||
fi
|
||
|
||
if [ ! -f "${binary_path}" ]; then
|
||
error "解压后未找到二进制文件"
|
||
exit 1
|
||
fi
|
||
|
||
chmod +x "${binary_path}"
|
||
|
||
# ---------- 安装 ----------
|
||
step "安装到 ${INSTALL_DIR}/${BINARY_NAME}"
|
||
|
||
if [ ! -w "${INSTALL_DIR}" ]; then
|
||
warn "需要管理员权限写入 ${INSTALL_DIR}"
|
||
sudo mkdir -p "${INSTALL_DIR}"
|
||
sudo mv "${binary_path}" "${INSTALL_DIR}/${BINARY_NAME}"
|
||
sudo chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
|
||
else
|
||
mkdir -p "${INSTALL_DIR}"
|
||
mv "${binary_path}" "${INSTALL_DIR}/${BINARY_NAME}"
|
||
fi
|
||
|
||
info "二进制: ${INSTALL_DIR}/${BINARY_NAME}"
|
||
}
|
||
|
||
# ---------- 安装 skills ----------
|
||
install_skills() {
|
||
local version="$1"
|
||
step "安装 skills..."
|
||
|
||
mkdir -p "${SKILLS_DIR}"
|
||
|
||
local skills_url="${API_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases/${version}/assets/${BINARY_NAME}_${version}_skills.tar.gz"
|
||
local tmpdir
|
||
tmpdir="$(mktemp -d)"
|
||
|
||
if download_with_retry "${skills_url}" "${tmpdir}/skills.tar.gz" 2>/dev/null; then
|
||
tar -xzf "${tmpdir}/skills.tar.gz" -C "${SKILLS_DIR}" 2>/dev/null || true
|
||
info "Skills 安装到: ${SKILLS_DIR}"
|
||
else
|
||
warn "Skills 包不可用(可稍后通过 gitlink-cli-install-skills 安装)"
|
||
fi
|
||
|
||
rm -rf "${tmpdir}"
|
||
}
|
||
|
||
# ---------- 验证安装 ----------
|
||
verify() {
|
||
step "验证安装..."
|
||
if command -v "${BINARY_NAME}" >/dev/null 2>&1; then
|
||
info "安装成功! $("${BINARY_NAME}" version 2>/dev/null || echo "${BINARY_NAME}")"
|
||
elif [ -x "${INSTALL_DIR}/${BINARY_NAME}" ]; then
|
||
info "安装成功! $("${INSTALL_DIR}/${BINARY_NAME}" version 2>/dev/null || echo "${INSTALL_DIR}/${BINARY_NAME}")"
|
||
warn "请将 ${INSTALL_DIR} 添加到 PATH: export PATH=${INSTALL_DIR}:\$PATH"
|
||
else
|
||
error "安装验证失败"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# ---------- 显示帮助 ----------
|
||
show_help() {
|
||
cat << EOF
|
||
GitLink CLI 安装脚本
|
||
|
||
用法:
|
||
curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash [选项]
|
||
|
||
选项:
|
||
VERSION=0.1.0 指定版本
|
||
INSTALL_DIR=/path 指定安装目录
|
||
DEBUG=true 显示调试信息
|
||
|
||
环境变量:
|
||
INSTALL_DIR 安装目录(默认: auto,自动选择)
|
||
VERSION 版本(默认: latest)
|
||
|
||
命令:
|
||
list 列出可用版本
|
||
uninstall 卸载
|
||
rollback VERSION 回滚到指定版本
|
||
|
||
示例:
|
||
# 安装最新版本
|
||
curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash
|
||
|
||
# 安装指定版本
|
||
VERSION=0.1.0 curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash
|
||
|
||
# 安装到用户目录
|
||
INSTALL_DIR=$HOME/.local/bin curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash
|
||
|
||
# 列出可用版本
|
||
curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash -s -- list
|
||
|
||
# 卸载
|
||
curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash -s -- uninstall
|
||
EOF
|
||
}
|
||
|
||
# ---------- main ----------
|
||
main() {
|
||
echo ""
|
||
echo " ╔══════════════════════════════════════╗"
|
||
echo " ║ GitLink CLI 一键安装 ║"
|
||
echo " ╚══════════════════════════════════════╝"
|
||
echo ""
|
||
|
||
# 处理命令行参数
|
||
case "${1:-}" in
|
||
list)
|
||
list_versions
|
||
exit 0
|
||
;;
|
||
uninstall)
|
||
INSTALL_DIR="${INSTALL_DIR:-$(select_install_dir)}"
|
||
uninstall
|
||
;;
|
||
rollback)
|
||
rollback_version "$2"
|
||
;;
|
||
help|--help|-h)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
esac
|
||
|
||
# 环境检测
|
||
check_environment
|
||
|
||
# 选择安装目录
|
||
INSTALL_DIR=$(select_install_dir)
|
||
export INSTALL_DIR
|
||
|
||
local platform
|
||
platform=$(detect_platform)
|
||
info "检测到平台: ${platform}"
|
||
|
||
local version
|
||
if [ "${VERSION}" = "latest" ]; then
|
||
version=$(fetch_latest_version)
|
||
info "最新版本: ${version}"
|
||
else
|
||
version="${VERSION}"
|
||
info "指定版本: ${version}"
|
||
fi
|
||
|
||
download_binary "${platform}" "${version}"
|
||
install_skills "${version}"
|
||
verify
|
||
|
||
echo ""
|
||
info "快速开始:"
|
||
info " gitlink-cli auth login # 登录账号"
|
||
info " gitlink-cli --help # 查看所有命令"
|
||
info " gitlink-cli version # 查看版本信息"
|
||
echo ""
|
||
}
|
||
|
||
main "$@"
|