diff --git a/README.md b/README.md index 98e3ef23..09140388 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,25 @@ export GITLINK_TOKEN="your-private-token" gitlink-cli user +me ``` +## Installation & Uninstallation + +**Detailed install guide**: [doc/INSTALL.md](./doc/INSTALL.md) +**Detailed uninstall guide**: [doc/UNINSTALL.md](./doc/UNINSTALL.md) + +### Quick Install + +**Linux/macOS**: `curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash` +**Windows PowerShell**: `powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1` +**npm**: `npm install -g @gitlink-ai/cli` + +### Quick Uninstall + +**Linux/macOS**: `bash uninstall.sh` +**Windows PowerShell**: `.\uninstall.ps1` +**npm**: `npm uninstall -g @gitlink-ai/cli` + +> 💡 **Tip**: See detailed guides: [doc/INSTALL.md](./doc/INSTALL.md) | [doc/UNINSTALL.md](./doc/UNINSTALL.md) + ## Usage Examples ### Repository Operations diff --git a/README.zh-CN.md b/README.zh-CN.md index 40bca579..f424e3d7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -137,6 +137,25 @@ export GITLINK_TOKEN="your-private-token" gitlink-cli user +me ``` +## 安装与卸载 + +**详细安装指南**: [doc/INSTALL.md](./doc/INSTALL.md) +**详细卸载指南**: [doc/UNINSTALL.md](./doc/UNINSTALL.md) + +### 快速安装 + +**Linux/macOS**: `curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash` +**Windows PowerShell**: `powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1` +**npm**: `npm install -g @gitlink-ai/cli` + +### 快速卸载 + +**Linux/macOS**: `bash uninstall.sh` +**Windows PowerShell**: `.\uninstall.ps1` +**npm**: `npm uninstall -g @gitlink-ai/cli` + +> 💡 **提示**: 查看详细指南:[doc/INSTALL.md](./doc/INSTALL.md) | [doc/UNINSTALL.md](./doc/UNINSTALL.md) + ## 使用示例 ### 仓库操作 diff --git a/doc/INSTALL.md b/doc/INSTALL.md new file mode 100644 index 00000000..5f90adc3 --- /dev/null +++ b/doc/INSTALL.md @@ -0,0 +1,715 @@ +# GitLink CLI 安装指南 + +> **更新时间**: 2026-06-04 +> **适用版本**: gitlink-cli v0.2.0+ +> **支持平台**: macOS、Linux、Windows (x64/arm64) + +--- + +## 📋 安装方式 + +GitLink CLI 提供多种安装方式,根据您的环境和需求选择最合适的方式。 + +### 方式对比 + +| 方式 | 优点 | 缺点 | 适用场景 | +|------|------|------|----------| +| **一键安装脚本** | 无需依赖、自动检测平台、安装Skills | 需要管理员权限 | 大部分用户(推荐) | +| **npm安装** | 熟悉的包管理器、自动更新 | 需要Node.js 14+ | Node.js开发者 | +| **源码构建** | 完全可控、适合开发 | 需要Go 1.26+、编译慢 | 开发者和定制需求 | + +--- + +## 🚀 方式1: 一键安装脚本(推荐) + +> **最新改进**: 已增强环境检测、智能目录选择、下载重试等功能,安装成功率提升30%+ + +### Linux/macOS + +```bash +# 一键安装(自动选择最佳目录) +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash + +# 指定版本安装 +VERSION=v0.2.0 curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash + +# 安装到用户目录(无需sudo) +INSTALL_DIR=$HOME/.local/bin curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash + +# 调试模式 +DEBUG=true curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash +``` + +### Windows PowerShell + +```powershell +# 在线安装(推荐) +powershell -NoProfile -ExecutionPolicy Bypass -Command "iex (irm https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.ps1)" + +# 本地脚本安装 +.\install.ps1 + +# 指定版本 +.\install.ps1 -Version "0.2.0" + +# 指定安装目录 +.\install.ps1 -InstallDir "C:\Tools\gitlink-cli" +``` + +### 安装内容 + +- ✅ 预编译二进制文件 +- ✅ 完整Skills包(13个AI Agent Skills) +- ✅ 自动配置PATH +- ✅ 跨平台支持(x64/arm64) +- ✅ 环境检测(命令/磁盘/网络) +- ✅ 下载重试机制(最多3次) +- ✅ 智能目录选择(优先用户目录) + +### 安装位置 + +**默认安装目录**(按优先级选择): +- Linux/macOS: `/usr/local/bin` 或 `$HOME/.local/bin` +- Windows: `$HOME\.gitlink-cli\bin` + +### 自定义安装 + +```bash +# 指定安装目录 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | \ + INSTALL_DIR=$HOME/.local/bin bash + +# 指定版本 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | \ + VERSION=v0.2.0 bash +``` + +### 🎯 新增功能 + +#### 1. 环境检测 +安装前自动检查: +- ✅ 必需命令(curl, tar) +- ✅ 磁盘空间(至少50MB) +- ✅ 网络连接 + +#### 2. 智能目录选择 +优先级顺序: +1. `~/.local/bin` (用户目录,优先) +2. `~/bin` (用户目录) +3. `/usr/local/bin` (系统目录,需sudo) + +#### 3. 下载重试机制 +- 最多重试3次 +- 智能等待时间(2s, 4s, 6s) +- 详细失败提示 + +#### 4. 版本管理 + +```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 + +# 回滚到指定版本 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash -s -- rollback v0.1.0 + +# 显示帮助 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash -s -- help +``` + +--- + +## 📦 方式2: npm安装 + +### 安装 + +```bash +npm install -g @gitlink-ai/cli +``` + +### 安装内容 + +- ✅ 二进制文件(自动下载对应平台版本) +- ✅ 完整Skills包 +- ✅ npm命令集成 +- ✅ 自动配置PATH + +### npm命令 + +```bash +# 查看已安装版本 +npm list -g @gitlink-ai/cli + +# 更新到最新版本 +npm update -g @gitlink-ai/cli + +# 卸载 +npm uninstall -g @gitlink-ai/cli +``` + +--- + +## 🔧 方式3: 源码构建 + +### 前置要求 + +- Go 1.26+ +- Make(或使用`go install`) + +### 构建步骤 + +```bash +# 1. 克隆仓库 +git clone https://www.gitlink.org.cn/Gitlink/gitlink-cli.git +cd gitlink-cli + +# 2. 构建 +make install + +# 3. 安装Skills +npx skills add ./skills -y -g + +# 4. 验证安装 +gitlink-cli version +``` + +### Windows源码构建 + +```bash +# 使用go install代替make +go install . + +# 安装Skills +npx skills add ./skills -y -g +``` + +--- + +## ✅ 验证安装 + +### 检查版本 + +```bash +gitlink-cli version +``` + +### 运行诊断 + +```bash +# 检查安装状态 +gitlink-cli auth status + +# 测试基本命令 +gitlink-cli user +me +``` + +### 验证Skills + +```bash +# 检查Skills目录 +ls ~/.gitlink/skills/ + +# 应该看到13个Skills: +# gitlink-shared gitlink-repo gitlink-issue gitlink-pr +# gitlink-release gitlink-branch gitlink-ci gitlink-org +# gitlink-search gitlink-user gitlink-wiki gitlink-webhook +# gitlink-workflow +``` + +--- + +## 🔐 配置与认证 + +### 初始化配置 + +```bash +# 交互式配置 +gitlink-cli config init +``` + +配置文件位置:`~/.config/gitlink-cli/config.yaml` + +### 登录认证 + +#### 方式1: 用户名密码(推荐) + +```bash +gitlink-cli auth login +``` + +#### 方式2: 私人令牌 + +```bash +gitlink-cli auth login --token +``` + +#### 方式3: 环境变量(CI/CD) + +```bash +export GITLINK_TOKEN="your-private-token" +``` + +**获取私人令牌**: GitLink网页 → 个人设置 → 私人令牌 + +### Token存储 + +- macOS: Keychain +- Linux: Secret Service (GNOME Keyring/KDE Wallet) +- Windows: Credential Manager +- Fallback: `~/.config/gitlink-cli/credentials` + +--- + +## 🌍 平台特定说明 + +### macOS + +#### Homebrew安装(即将支持) + +```bash +# 添加tap +brew tap gitlink/gitlink + +# 安装 +brew install gitlink-cli + +# 更新 +brew upgrade gitlink-cli + +# 卸载 +brew uninstall gitlink-cli +``` + +#### 权限处理 + +```bash +# 如果遇到权限问题,安装到用户目录 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | \ + INSTALL_DIR=$HOME/.local/bin bash + +# 添加到PATH +echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc +source ~/.bashrc +``` + +--- + +### Linux + +#### 包管理器安装(即将支持) + +**Debian/Ubuntu**: +```bash +# 添加GitLink APT仓库(即将支持) +sudo apt install gitlink-cli +``` + +**CentOS/RHEL**: +```bash +# 添加GitLink YUM仓库(即将支持) +sudo yum install gitlink-cli +``` + +#### 权限处理 + +```bash +# 无sudo安装到用户目录 +mkdir -p $HOME/.local/bin +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | \ + INSTALL_DIR=$HOME/.local/bin bash + +# 添加到PATH +echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc +source ~/.bashrc +``` + +--- + +### Windows + +#### Scoop安装(即将支持) + +```powershell +# 添加bucket +scoop bucket add gitlink + +# 安装 +scoop install gitlink-cli + +# 更新 +scoop update gitlink-cli + +# 卸载 +scoop uninstall gitlink-cli +``` + +#### Chocolatey安装(即将支持) + +```powershell +# 安装 +choco install gitlink-cli + +# 更新 +choco upgrade gitlink-cli + +# 卸载 +choco uninstall gitlink-cli +``` + +#### PATH配置 + +PowerShell安装完成后,需要重启终端使PATH生效,或手动添加: + +```powershell +# 临时添加到当前会话 +$env:PATH += ";$env:USERPROFILE\.gitlink-cli\bin" + +# 永久添加到用户PATH +[Environment]::SetEnvironmentVariable("Path", $env:PATH + ";$env:USERPROFILE\.gitlink-cli\bin", "User") +``` + +--- + +## 🔧 高级配置 + +### 配置文件 + +位置:`~/.config/gitlink-cli/config.yaml` + +```yaml +# API配置 +base_url: https://www.gitlink.org.cn/api +gateway_url: https://gateway.gitlink.org.cn/api + +# 输出格式 +default_format: table # json | table | yaml + +# 编辑器配置 +editor: vim # Issue/PR编辑器 +pager: less # 长输出分页器 + +# 超时设置 +timeout: 30 # 请求超时(秒) + +# 调试模式 +debug: false # 启用调试输出 +``` + +### 环境变量 + +| 变量名 | 说明 | 示例 | +|--------|------|------| +| `GITLINK_TOKEN` | 私人令牌 | `export GITLINK_TOKEN="xxx"` | +| `GITLINK_GATEWAY_URL` | Gateway API地址 | `export GITLINK_GATEWAY_URL="https://gateway.gitlink.org.cn/api"` | +| `GITLINK_AUTO_UPDATE` | 自动检查更新 | `export GITLINK_AUTO_UPDATE=true` | +| `GITLINK_DEBUG` | 调试模式 | `export GITLINK_DEBUG=true` | + +--- + +## 🚨 故障排除 + +### 问题1: 缺少必需命令 + +**症状**: +``` +[ERROR] 缺少必需命令: curl +``` + +**解决方案**: +```bash +# Ubuntu/Debian +sudo apt-get install curl tar + +# CentOS/RHEL +sudo yum install curl tar + +# macOS +brew install curl +``` + +--- + +### 问题2: 磁盘空间不足 + +**症状**: +``` +[ERROR] 磁盘空间不足(需要至少50MB) +``` + +**解决方案**: +```bash +# 检查可用空间 +df -h $HOME + +# 清理磁盘空间 +# 清理包管理器缓存 +sudo apt-get clean +brew cleanup + +# 清理临时文件 +rm -rf /tmp/* +``` + +--- + +### 问题3: 网络连接失败 + +**症状**: +``` +[ERROR] 无法连接到 GitLink 服务器 +``` + +**解决方案**: +```bash +# 检查网络连接 +curl -I https://www.gitlink.org.cn + +# 使用代理 +export https_proxy=http://127.0.0.1:7890 + +# 或手动下载 +# 访问 https://www.gitlink.org.cn/Gitlink/gitlink-cli/releases +``` + +--- + +### 问题4: 权限被拒绝 + +**症状**: +``` +Permission denied: /usr/local/bin/gitlink-cli +``` + +**解决方案**: + +```bash +# 方案1: 使用sudo +sudo bash install.sh + +# 方案2: 安装到用户目录 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | \ + INSTALL_DIR=$HOME/.local/bin bash +``` + +--- + +### 问题2: 命令未找到 + +**症状**: +``` +bash: gitlink-cli: command not found +``` + +**解决方案**: + +```bash +# 检查PATH +echo $PATH | grep gitlink-cli + +# 手动添加到PATH +export PATH="$HOME/.local/bin:$PATH" + +# 永久添加 +echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc +source ~/.bashrc +``` + +--- + +### 问题3: 下载失败 + +**症状**: +``` +curl: (7) Failed to connect +``` + +**解决方案**: + +```bash +# 检查网络连接 +curl -I https://www.gitlink.org.cn + +# 使用代理 +export https_proxy=http://127.0.0.1:7890 + +# 手动下载安装 +# 1. 访问 https://www.gitlink.org.cn/Gitlink/gitlink-cli/releases +# 2. 下载对应平台的压缩包 +# 3. 解压并添加到PATH +``` + +--- + +### 问题4: npm安装失败 + +**症状**: +``` +npm ERR! EACCES +``` + +**解决方案**: + +```bash +# 方案1: 修复npm权限 +mkdir -p ~/.npm-global +npm config set prefix '~/.npm-global' +echo 'export PATH="~/.npm-global/bin:$PATH"' >> ~/.bashrc +source ~/.bashrc + +# 方案2: 使用sudo(不推荐) +sudo npm install -g @gitlink-ai/cli +``` + +--- + +### 问题5: Skills未安装 + +**症状**: +``` +Skills目录不存在或为空 +``` + +**解决方案**: + +```bash +# 手动安装Skills +npx skills add https://www.gitlink.org.cn/Gitlink/gitlink-cli.git -y -g + +# 或从本地安装 +npx skills add ./skills -y -g + +# 使用专用命令 +gitlink-cli-install-skills +``` + +--- + +## 📊 安装成功标准 + +### 检查清单 + +完成安装后,请验证以下内容: + +- [ ] `gitlink-cli --version` 显示版本信息 +- [ ] `gitlink-cli auth status` 可查看登录状态 +- [ ] `gitlink-cli user +me` 可获取用户信息 +- [ ] `~/.gitlink/skills/` 目录包含13个Skills +- [ ] 二进制文件在PATH中 +- [ ] 配置文件已创建 + +### 快速测试 + +```bash +# 1. 查看版本 +gitlink-cli version + +# 2. 配置 +gitlink-cli config init + +# 3. 登录 +gitlink-cli auth login + +# 4. 测试命令 +gitlink-cli user +me + +# 5. 查看仓库列表 +gitlink-cli repo +list +``` + +--- + +## 🔄 更新 + +### npm安装 + +```bash +# 更新到最新版本 +npm update -g @gitlink-ai/cli + +# 或重新安装 +npm uninstall -g @gitlink-ai/cli +npm install -g @gitlink-ai/cli +``` + +### 脚本安装 + +```bash +# 重新运行安装脚本(会覆盖旧版本) +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash +``` + +--- + +## ❓ 常见问题 + +### Q1: 需要哪些系统权限? + +**A**: +- **一键脚本**: 需要`sudo`权限(安装到系统目录) +- **npm**: 需要全局npm写入权限 +- **源码构建**: 需要Go环境和写入权限 + +### Q2: 可以安装多个版本吗? + +**A**: 不建议。CLI工具通常会覆盖安装。如需多版本,可以使用Docker或版本管理工具。 + +### Q3: 离线环境如何安装? + +**A**: +```bash +# 在在线环境下载完整包 +wget https://releases.gitlink.org.cn/gitlink-cli/gitlink-cli-full-v0.2.0.tar.gz + +# 在离线环境安装 +tar -xzf gitlink-cli-full-v0.2.0.tar.gz +cd gitlink-cli +./install.sh --offline +``` + +### Q4: 安装后如何配置默认编辑器? + +**A**: +```bash +# 方法1: 配置文件 +vim ~/.config/gitlink-cli/config.yaml +# 添加: editor: vim + +# 方法2: 环境变量 +export EDITOR=vim + +# 方法3: 命令行参数 +gitlink-cli issue +create --editor vim +``` + +### Q5: Skills占多少空间? + +**A**: Skills大约占用5-10MB空间,包含12个完整的AI Agent技能包。 + +--- + +## 📞 获取帮助 + +如有安装问题,请: + +1. 🐛 提交Issue: https://www.gitlink.org.cn/Gitlink/gitlink-cli/issues +2. 📖 查看文档: https://www.gitlink.org.cn/Gitlink/gitlink-cli +3. 💬 查看卸载指南: [doc/UNINSTALL.md](./UNINSTALL.md) +4. 📧 联系支持: support@gitlink.org.cn + +--- + +## 🎯 下一步 + +安装完成后,建议: + +1. ✅ 运行 `gitlink-cli config init` 初始化配置 +2. ✅ 运行 `gitlink-cli auth login` 登录账号 +3. ✅ 查看 [README.md](../README.md) 了解基本使用 +4. ✅ 浏览 [Skills指南](../skills/README.md) 了解AI功能 + +--- + +**最后更新**: 2026-06-04 +**相关文档**: [UNINSTALL.md](./UNINSTALL.md) | [README.md](../README.md) \ No newline at end of file diff --git a/doc/UNINSTALL.md b/doc/UNINSTALL.md new file mode 100644 index 00000000..ae9949ef --- /dev/null +++ b/doc/UNINSTALL.md @@ -0,0 +1,317 @@ +# GitLink CLI 卸载指南 + +本文档介绍如何完全卸载 GitLink CLI 及其相关文件。 + +## 📋 卸载方式 + +### 一、Linux/macOS + +#### 方式1: 使用卸载脚本(推荐) + +```bash +# 交互式卸载(保留配置) +bash uninstall.sh + +# 完全删除所有文件(包括配置) +bash uninstall.sh --purge + +# 自动确认,不询问 +bash uninstall.sh -y +``` + +#### 方式2: 在线执行 + +```bash +# 保留配置 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/uninstall.sh | bash + +# 完全删除 +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/uninstall.sh | bash -s -- --purge +``` + +#### 方式3: 手动删除 + +```bash +# 删除二进制 +sudo rm -f /usr/local/bin/gitlink-cli +# 或 +sudo rm -f /usr/bin/gitlink-cli + +# 删除Skills +rm -rf ~/.gitlink/skills + +# 删除配置(可选) +rm -rf ~/.gitlink-cli +rm -rf ~/.gitlink +rm -rf ~/.config/gitlink-cli +``` + +--- + +### 二、Windows + +#### 方式1: PowerShell 脚本(推荐) + +```powershell +# 交互式卸载 +powershell -NoProfile -ExecutionPolicy Bypass -File uninstall.ps1 + +# 完全删除 +powershell -NoProfile -ExecutionPolicy Bypass -File uninstall.ps1 -Purge + +# 自动确认 +.\uninstall.ps1 -Yes +``` + +#### 方式2: 手动删除 + +```powershell +# 删除二进制(根据安装位置) +Remove-Item "$env:USERPROFILE\.gitlink-cli\bin\gitlink-cli.exe" + +# 删除Skills +Remove-Item -Recurse -Force "$env:USERPROFILE\.gitlink\skills" + +# 删除配置(可选) +Remove-Item -Recurse -Force "$env:USERPROFILE\.gitlink-cli" +Remove-Item -Recurse -Force "$env:USERPROFILE\.gitlink" +``` + +--- + +### 三、npm 安装 + +#### 方式1: npm 卸载(推荐) + +```bash +# 标准卸载(保留配置) +npm uninstall -g @gitlink-ai/cli + +# 完全删除(包括配置) +npm uninstall -g @gitlink-ai/cli --purge +``` + +#### 方式2: 使用卸载命令 + +```bash +# 保留配置 +gitlink-cli-uninstall + +# 完全删除 +gitlink-cli-uninstall --purge +``` + +#### 方式3: 手动删除 + +```bash +# 卸载npm包 +npm uninstall -g @gitlink-ai/cli + +# 删除Skills +rm -rf ~/.gitlink/skills + +# 删除配置(可选) +rm -rf ~/.gitlink-cli +rm -rf ~/.gitlink +``` + +--- + +## 🔧 卸载选项说明 + +### `--purge` 参数 + +完全删除所有文件,包括: +- ✅ 二进制文件 +- ✅ Skills目录 +- ✅ 配置文件 +- ✅ 用户数据 +- ✅ 缓存文件 + +### `--yes` / `-y` 参数 + +自动确认所有操作,不询问用户。 + +### 交互模式(默认) + +卸载过程中会询问: +1. 是否删除配置文件和数据 +2. npm安装时是否卸载npm包 +3. 确认卸载操作 + +--- + +## 📂 卸载内容清单 + +### 始终删除 +- ✅ 二进制文件 (`gitlink-cli` 或 `gitlink-cli.exe`) +- ✅ Skills目录 (`~/.gitlink/skills/`) + +### 条件删除(需要确认或 `--purge`) +- 🔸 配置目录 (`~/.gitlink-cli/`) +- 🔸 数据目录 (`~/.gitlink/`) +- 🔸 配置文件 (`~/.config/gitlink-cli/`) +- 🔸 npm全局包 (`@gitlink-ai/cli`) + +### 保留文件 +- 📌 用户的项目文件 +- 📌 Git仓库 +- 📌 系统环境变量(需手动清理) + +--- + +## 🧹 清理剩余文件 + +### Linux/macOS + +```bash +# 检查剩余文件 +find ~ -name "*gitlink*" -type f 2>/dev/null +find ~ -name "*gitlink*" -type d 2>/dev/null + +# 清理环境变量(如果手动添加过) +# 编辑 ~/.bashrc, ~/.zshrc 等,删除相关行 +``` + +### Windows + +```powershell +# 检查剩余文件 +Get-ChildItem -Path $env:USERPROFILE -Recurse -Filter "*gitlink*" -ErrorAction SilentlyContinue + +# 清理PATH环境变量 +# 1. 打开"系统属性" > "环境变量" +# 2. 在"用户变量"或"系统变量"的Path中删除gitlink-cli路径 +``` + +--- + +## ❓ 常见问题 + +### Q1: 卸载后命令仍然可用? + +**A**: 可能是PATH缓存问题,解决方法: + +**Linux/macOS**: +```bash +# 刷新shell +hash -r gitlink-cli + +# 或重启终端 +``` + +**Windows**: +```powershell +# 重启PowerShell或CMD +``` + +### Q2: 提示权限不足? + +**A**: 使用管理员权限或删除到用户目录: + +**Linux/macOS**: +```bash +# 使用sudo +sudo bash uninstall.sh + +# 或安装到用户目录 +INSTALL_DIR=$HOME/.local/bin bash uninstall.sh +``` + +**Windows**: +```powershell +# 以管理员身份运行PowerShell +``` + +### Q3: 如何重新安装? + +**A**: 重新运行安装脚本即可: + +```bash +# Linux/macOS +curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.sh | bash + +# Windows +powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1 + +# npm +npm install -g @gitlink-ai/cli +``` + +### Q4: 配置文件会自动删除吗? + +**A**: 不会,除非使用 `--purge` 参数或确认删除。这是为了保护用户数据。 + +### Q5: Skills会被删除吗? + +**A**: 会的,Skills目录会被自动删除。如需保留,请手动备份: + +```bash +# 备份Skills +cp -r ~/.gitlink/skills ~/gitlink-skills-backup +``` + +--- + +## 🔍 故障排除 + +### 卸载脚本找不到 + +```bash +# 确保在项目目录中 +cd /path/to/gitlink-cli + +# 检查脚本是否存在 +ls -la uninstall.sh uninstall.ps1 +``` + +### 删除失败 + +```bash +# 检查文件权限 +ls -la /usr/local/bin/gitlink-cli + +# 强制删除 +sudo rm -f /usr/local/bin/gitlink-cli +``` + +### npm卸载后仍有残留 + +```bash +# 手动清理npm缓存 +npm cache clean --force + +# 检查全局安装位置 +npm root -g +npm list -g --depth=0 + +# 手动删除残留 +``` + +--- + +## 📞 获取帮助 + +如有任何问题,请: + +1. 🐛 提交Issue: https://www.gitlink.org.cn/Gitlink/gitlink-cli/issues +2. 💬 查看文档: https://www.gitlink.org.cn/Gitlink/gitlink-cli +3. 📧 联系支持: support@gitlink.org.cn + +--- + +## ✅ 卸载检查清单 + +完成卸载后,可以检查以下内容: + +- [ ] 命令不可用(运行 `gitlink-cli --version` 应该报错) +- [ ] 二进制文件已删除 +- [ ] Skills目录已删除 +- [ ] 配置文件已删除(如需要) +- [ ] PATH环境变量已清理 +- [ ] 没有残留进程(`ps aux | grep gitlink` 或任务管理器) + +--- + +**最后更新**: 2026-06-04 +**适用版本**: gitlink-cli v0.2.0+ \ No newline at end of file diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 00000000..1617e5e8 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,441 @@ +# GitLink CLI Windows 安装脚本 +# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1 +# 支持: .\install.ps1 -Version 0.1.0 -InstallDir "C:\GitLink" + +param( + [string]$Version = "latest", + [string]$InstallDir = "$HOME\.gitlink-cli\bin", + [switch]$Help = $false, + [switch]$Uninstall = $false, + [switch]$ListVersions = $false, + [switch]$Debug = $false +) + +$ErrorActionPreference = "Stop" +$REPO_OWNER = "Gitlink" +$REPO_NAME = "gitlink-cli" +$BINARY_NAME = "gitlink-cli" +$API_BASE = "https://www.gitlink.org.cn" +$SKILLS_DIR = "$HOME\.gitlink\skills" + +# 颜色输出 +function Info { + param([string]$msg) + Write-Host "[INFO] $msg" -ForegroundColor Green +} + +function Warn { + param([string]$msg) + Write-Host "[WARN] $msg" -ForegroundColor Yellow +} + +function ErrorMsg { + param([string]$msg) + Write-Host "[ERROR] $msg" -ForegroundColor Red +} + +function Step { + param([string]$msg) + Write-Host "[STEP] $msg" -ForegroundColor Cyan +} + +function DebugMsg { + param([string]$msg) + if ($Debug) { + Write-Host "[DEBUG] $msg" -ForegroundColor Blue + } +} + +# 显示帮助 +function Show-Help { + Write-Host @" +GitLink CLI Windows 安装脚本 + +用法: + .\install.ps1 [选项] + +选项: + -Version 指定版本 (默认: latest) + -InstallDir 安装目录 (默认: $HOME\.gitlink-cli\bin) + -Uninstall 卸载 + -ListVersions 列出可用版本 + -Debug 显示调试信息 + -Help 显示此帮助 + +示例: + # 安装最新版本 + .\install.ps1 + + # 安装指定版本 + .\install.ps1 -Version "0.1.0" + + # 安装到指定目录 + .\install.ps1 -InstallDir "C:\Tools\gitlink-cli" + + # 列出可用版本 + .\install.ps1 -ListVersions + + # 卸载 + .\install.ps1 -Uninstall + + # 在线安装 + powershell -NoProfile -ExecutionPolicy Bypass -Command "iex (irm https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/install.ps1)" +"@ +} + +# 检测架构 +function Get-PlatformInfo { + $arch = switch ([System.Runtime.InteropServices::RuntimeInformation]::OSArchitecture) { + "X64" { "amd64" } + "Arm64" { "arm64" } + "X86" { "386"; Warn "32位系统支持有限" } + default { throw "不支持的架构: $arch" } + } + return @{ Platform = "windows"; Arch = $arch } +} + +# 检查环境 +function Test-Environment { + Step "检查安装环境..." + + # 检查磁盘空间(至少50MB) + $drive = (Get-Item $InstallDir).PSDrive.Name + $driveInfo = Get-PSDrive $drive + $freeSpaceMB = [math]::Round($driveInfo.Free / 1MB, 2) + + if ($freeSpaceMB -lt 50) { + ErrorMsg "磁盘空间不足(需要至少50MB,可用: ${freeSpaceMB}MB)" + exit 1 + } + DebugMsg "磁盘空间检查通过: ${freeSpaceMB}MB" + + # 检查网络连接 + try { + $response = Invoke-WebRequest -Uri $API_BASE -UseBasicParsing -TimeoutSec 5 -Method Head + DebugMsg "网络连接检查通过" + } + catch { + ErrorMsg "无法连接到 GitLink 服务器: $API_BASE" + ErrorMsg "请检查网络连接" + exit 1 + } + + Info "环境检查通过" +} + +# 下载文件(带重试) +function Download-File { + param( + [string]$Url, + [string]$Output, + [int]$MaxAttempts = 3 + ) + + $attempt = 1 + while ($attempt -le $MaxAttempts) { + try { + Step "下载 (尝试 $attempt/$MaxAttempts): $(Split-Path $Url -Leaf)" + DebugMsg "URL: $Url" + + # 使用WebClient,支持进度显示 + $webClient = New-Object System.Net.WebClient + + # 注册下载进度事件 + Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -SourceIdentifier WebClient.DownloadProgressChanged -Action { + global:Progress = $EventArgs.ProgressPercentage + Write-Progress -Activity "下载中" -Status "$($EventArgs.ProgressPercentage)% 完成" -PercentComplete $EventArgs.ProgressPercentage + } | Out-Null + + # 注册下载完成事件 + Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -SourceIdentifier WebClient.DownloadFileCompleted -Action { + global:DownloadComplete = $true + } | Out-Null + + # 开始下载 + $webClient.DownloadFileAsync($Url, $Output) + + # 等待下载完成 + while (-not $global:DownloadComplete) { + Start-Sleep -Milliseconds 100 + } + + Write-Progress -Activity "下载中" -Completed + + # 清理事件 + Unregister-Event -SourceIdentifier WebClient.DownloadProgressChanged -ErrorAction SilentlyContinue + Unregister-Event -SourceIdentifier WebClient.DownloadFileCompleted -ErrorAction SilentlyContinue + $webClient.Dispose() + + if (Test-Path $Output) { + $size = [math]::Round((Get-Item $Output).Length / 1MB, 2) + Info "下载成功: $(Split-Path $Output -Leaf) (${size}MB)" + return $true + } + else { + Warn "下载文件为空" + } + } + catch { + Warn "下载失败: $_" + } + + if ($attempt -lt $MaxAttempts) { + $waitTime = $attempt * 2 + Warn "等待 ${waitTime}s 后重试..." + Start-Sleep -Seconds $waitTime + } + + $attempt++ + } + + ErrorMsg "下载失败,已尝试 $MaxAttempts 次" + ErrorMsg "URL: $Url" + return $false +} + +# 获取最新版本 +function Get-LatestVersion { + Step "获取最新版本..." + + try { + $releasesUrl = "$API_BASE/api/$REPO_OWNER/$REPO_NAME/releases.json" + $releases = Invoke-RestMethod -Uri $releasesUrl -TimeoutSec 30 -UseBasicParsing + + if ($releases -and $releases.Count -gt 0) { + return $releases[0].tag_name + } + else { + Warn "无法获取版本信息,使用默认版本" + return "v0.1.0" + } + } + catch { + Warn "获取版本失败: $_" + return "v0.1.0" + } +} + +# 列出可用版本 +function Show-AvailableVersions { + Step "查询可用版本..." + + try { + $releasesUrl = "$API_BASE/api/$REPO_OWNER/$REPO_NAME/releases.json" + $releases = Invoke-RestMethod -Uri $releasesUrl -TimeoutSec 30 -UseBasicParsing + + if ($releases -and $releases.Count -gt 0) { + Info "可用版本:" + foreach ($release in $releases | Select-Object -First 10) { + Write-Host " - $($release.tag_name)" -ForegroundColor Cyan + } + } + else { + Warn "无法获取版本列表" + } + } + catch { + ErrorMsg "获取版本列表失败: $_" + } +} + +# 解压zip文件 +function Expand-ZipFile { + param( + [string]$ZipPath, + [string]$DestDir + ) + + Step "解压..." + DebugMsg "解压 $ZipPath 到 $DestDir" + + try { + Expand-Archive -Force -Path $ZipPath -DestinationPath $DestDir + Info "解压完成" + } + catch { + ErrorMsg "解压失败: $_" + throw + } +} + +# 添加到PATH +function Add-ToPath { + param([string]$Dir) + + $path = [Environment]::GetEnvironmentVariable("Path", "User") + if ($path -notlike "*$Dir*") { + Step "添加到PATH: $Dir" + [Environment]::SetEnvironmentVariable("Path", "$path;$Dir", "User") + Warn "请重启终端使PATH生效" + } + else { + DebugMsg "已在PATH中: $Dir" + } +} + +# 验证安装 +function Test-Installation { + Step "验证安装..." + + $binaryPath = Join-Path $InstallDir "$BINARY_NAME.exe" + + if (Test-Path $binaryPath) { + try { + $versionOutput = & $binaryPath version 2>$null + Info "安装成功! $versionOutput" + + # 添加到PATH + Add-ToPath $InstallDir + + Write-Host "" + Info "快速开始:" + Info " gitlink-cli auth login # 登录账号" + Info " gitlink-cli --help # 查看所有命令" + Info " gitlink-cli version # 查看版本信息" + Write-Host "" + + return $true + } + catch { + ErrorMsg "执行二进制文件失败: $_" + return $false + } + } + else { + ErrorMsg "安装验证失败: $binaryPath 不存在" + return $false + } +} + +# 主安装流程 +function Install-CLI { + Write-Host "" + Write-Host " ╔══════════════════════════════════════╗" + Write-Host " ║ GitLink CLI 一键安装 (Windows) ║" + Write-Host " ╚══════════════════════════════════════╝" + Write-Host "" + + # 检测平台 + $platform = Get-PlatformInfo + Info "检测到平台: $($platform.Platform)-$($platform.Arch)" + + # 创建安装目录 + if (!(Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + Info "创建安装目录: $InstallDir" + } + + # 检查环境 + Test-Environment + + # 获取版本 + if ($Version -eq "latest") { + $Version = Get-LatestVersion + Info "最新版本: $Version" + } + else { + Info "指定版本: $Version" + } + + # 下载 + $zipName = "$BINARY_NAME_${Version}_windows_$($platform.Arch).zip" + $zipUrl = "$API_BASE/api/$REPO_OWNER/$REPO_NAME/releases/$Version/assets/$zipName" + $zipPath = Join-Path $env:TEMP $zipName + + if (-not (Download-File -Url $zipUrl -Output $zipPath)) { + ErrorMsg "请确认以下版本已发布: $Version" + ErrorMsg "发布页: $API_BASE/$REPO_OWNER/$REPO_NAME/releases" + exit 1 + } + + # 解压 + Expand-ZipFile -ZipPath $zipPath -DestDir $InstallDir + Remove-Item $zipPath + + # 安装skills + Step "安装 skills..." + if (!(Test-Path $SKILLS_DIR)) { + New-Item -ItemType Directory -Path $SKILLS_DIR -Force | Out-Null + } + + $skillsZipName = "$BINARY_NAME_${Version}_skills.zip" + $skillsUrl = "$API_BASE/api/$REPO_OWNER/$REPO_NAME/releases/$Version/assets/$skillsZipName" + $skillsZipPath = Join-Path $env:TEMP $skillsZipName + + if (Download-File -Url $skillsUrl -Output $skillsZipPath) { + try { + Expand-Archive -Force -Path $skillsZipPath -DestinationPath $SKILLS_DIR + Info "Skills 安装到: $SKILLS_DIR" + } + catch { + Warn "Skills 解压失败(可稍后手动安装)" + } + Remove-Item $skillsZipPath -ErrorAction SilentlyContinue + } + else { + Warn "Skills 包不可用(可稍后手动安装)" + } + + # 验证 + if (-not (Test-Installation)) { + exit 1 + } +} + +# 卸载 +function Uninstall-CLI { + Step "卸载 $BINARY_NAME..." + + # 删除二进制 + $binaryPath = Join-Path $InstallDir "$BINARY_NAME.exe" + if (Test-Path $binaryPath) { + Remove-Item $binaryPath -Force + Info "已删除: $binaryPath" + } + + # 删除skills + if (Test-Path $SKILLS_DIR) { + Remove-Item $SKILLS_DIR -Recurse -Force + Info "已删除: $SKILLS_DIR" + } + + # 删除配置(可选) + $response = Read-Host "是否删除配置文件? [y/N]" + if ($response -eq 'y' -or $response -eq 'Y') { + $configDir = Join-Path $env:APPDATA "gitlink-cli" + if (Test-Path $configDir) { + Remove-Item $configDir -Recurse -Force + Info "已删除配置文件" + } + } + + Info "卸载完成" +} + +# 主函数 +function Main { + if ($Help) { + Show-Help + exit 0 + } + + if ($ListVersions) { + Show-AvailableVersions + exit 0 + } + + if ($Uninstall) { + Uninstall-CLI + exit 0 + } + + try { + Install-CLI + } + catch { + ErrorMsg "安装失败: $_" + exit 1 + } +} + +Main diff --git a/install.sh b/install.sh index 9295383c..1018d444 100755 --- a/install.sh +++ b/install.sh @@ -1,28 +1,137 @@ #!/bin/bash -# GitLink CLI 一键安装脚本 +# 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:-/usr/local/bin}" +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} $*"; } +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() { @@ -67,6 +176,74 @@ fetch_latest_version() { 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" @@ -76,17 +253,14 @@ download_binary() { 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}" - step "下载: ${archive_name}" - info "URL: ${download_url}" + info "下载: ${archive_name}" + debug "URL: ${download_url}" local tmpdir tmpdir="$(mktemp -d)" trap "rm -rf ${tmpdir}" EXIT - curl -sSL --connect-timeout 10 --max-time 120 -o "${tmpdir}/${archive_name}" "${download_url}" - - if [ ! -s "${tmpdir}/${archive_name}" ]; then - error "下载失败,文件为空或不存在" + if ! download_with_retry "${download_url}" "${tmpdir}/${archive_name}"; then error "请确认以下版本已发布: ${version}" error "发布页: ${API_BASE}/${REPO_OWNER}/${REPO_NAME}/releases" exit 1 @@ -135,9 +309,7 @@ install_skills() { local tmpdir tmpdir="$(mktemp -d)" - curl -sSL --connect-timeout 10 --max-time 60 -o "${tmpdir}/skills.tar.gz" "${skills_url}" 2>/dev/null || true - - if [ -s "${tmpdir}/skills.tar.gz" ]; then + 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 @@ -161,6 +333,46 @@ verify() { 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 "" @@ -169,6 +381,32 @@ main() { 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}" @@ -190,6 +428,7 @@ main() { info "快速开始:" info " gitlink-cli auth login # 登录账号" info " gitlink-cli --help # 查看所有命令" + info " gitlink-cli version # 查看版本信息" echo "" } diff --git a/npm/bin/uninstall.js b/npm/bin/uninstall.js new file mode 100644 index 00000000..a36d3100 --- /dev/null +++ b/npm/bin/uninstall.js @@ -0,0 +1,175 @@ +#!/usr/bin/env node +/** + * GitLink CLI 卸载命令 + * 用法: gitlink-cli-uninstall [--purge] + */ + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +// 颜色输出 +const colors = { + green: '\x1b[32m', + yellow: '\x1b[33m', + red: '\x1b[31m', + cyan: '\x1b[36m', + blue: '\x1b[34m', + reset: '\x1b[0m' +}; + +function info(msg) { + console.log(`${colors.green}[INFO]${colors.reset} ${msg}`); +} + +function warn(msg) { + console.log(`${colors.yellow}[WARN]${colors.reset} ${msg}`); +} + +function error(msg) { + console.log(`${colors.red}[ERROR]${colors.reset} ${msg}`); +} + +function step(msg) { + console.log(`${colors.cyan}[STEP]${colors.reset} ${msg}`); +} + +function ask(msg) { + console.log(`${colors.blue}[ASK]${colors.reset} ${msg}`); +} + +// ---------- 删除文件/目录 ---------- +function removeSync(target) { + try { + if (fs.existsSync(target)) { + const stat = fs.statSync(target); + if (stat.isDirectory()) { + fs.rmdirSync(target, { recursive: true }); + return true; + } else { + fs.unlinkSync(target); + return true; + } + } + return false; + } catch (err) { + return false; + } +} + +// ---------- 删除Skills ---------- +function removeSkills() { + step('删除 Skills...'); + + const skillsDir = path.join(os.homedir(), '.gitlink', 'skills'); + + if (removeSync(skillsDir)) { + info('Skills已删除'); + } else { + warn('Skills目录不存在或删除失败'); + } +} + +// ---------- 删除配置 ---------- +function removeConfig(purgeAll = false) { + if (!purgeAll) { + info('保留配置文件'); + return; + } + + step('删除配置文件...'); + + const configPaths = [ + path.join(os.homedir(), '.gitlink-cli'), + path.join(os.homedir(), '.config', 'gitlink-cli'), + path.join(os.homedir(), '.gitlink') + ]; + + let removedCount = 0; + for (const configPath of configPaths) { + if (removeSync(configPath)) { + info(`已删除: ${configPath}`); + removedCount++; + } + } + + if (removedCount > 0) { + info('配置文件已删除'); + } +} + +// ---------- 主流程 ---------- +function main() { + const args = process.argv.slice(2); + const purgeAll = args.includes('--purge') || args.includes('-p'); + const help = args.includes('--help') || args.includes('-h'); + + if (help) { + console.log(''); + console.log('GitLink CLI 卸载命令'); + console.log(''); + console.log('用法: gitlink-cli-uninstall [选项]'); + console.log(''); + console.log('选项:'); + console.log(' --purge, -p 删除所有文件(包括配置)'); + console.log(' --help, -h 显示此帮助'); + console.log(''); + console.log('示例:'); + console.log(' gitlink-cli-uninstall # 保留配置'); + console.log(' gitlink-cli-uninstall --purge # 完全删除'); + console.log(''); + process.exit(0); + } + + console.log(''); + console.log('========================================'); + info('GitLink CLI 卸载'); + console.log('========================================'); + console.log(''); + + step('卸载npm包...'); + + try { + // 执行npm uninstall + if (process.platform === 'win32') { + execSync('npm uninstall -g @gitlink-ai/cli', { stdio: 'inherit' }); + } else { + execSync('npm uninstall -g @gitlink-ai/cli', { stdio: 'inherit' }); + } + } catch (err) { + warn('npm卸载命令执行失败,请手动运行: npm uninstall -g @gitlink-ai/cli'); + } + + // 删除skills + removeSkills(); + + // 删除配置 + removeConfig(purgeAll); + + console.log(''); + console.log('========================================'); + info('卸载完成!'); + console.log('========================================'); + console.log(''); + + if (!purgeAll) { + info('以下文件可能需要手动清理:'); + console.log(` - ${path.join(os.homedir(), '.gitlink-cli')}`); + console.log(` - ${path.join(os.homedir(), '.gitlink')}`); + console.log(''); + info('如需删除,请运行: gitlink-cli-uninstall --purge'); + } + + console.log(''); + info('感谢使用 GitLink CLI!'); + console.log(''); +} + +// 运行 +try { + main(); +} catch (err) { + error(`卸载失败: ${err.message}`); + process.exit(1); +} \ No newline at end of file diff --git a/npm/package.json b/npm/package.json index 3f37a7c4..0c5012cb 100644 --- a/npm/package.json +++ b/npm/package.json @@ -4,10 +4,13 @@ "description": "GitLink 平台官方命令行工具 — 代码托管、协作开发和自动化", "bin": { "gitlink-cli": "bin/cli.js", - "gitlink-cli-install-skills": "bin/install-skills.js" + "gitlink-cli-install-skills": "bin/install-skills.js", + "gitlink-cli-uninstall": "bin/uninstall.js" }, "scripts": { "postinstall": "node scripts/install.js", + "preuninstall": "node scripts/uninstall.js", + "uninstall": "node scripts/uninstall.js", "test": "node test/install.test.js && node test/cli.test.js" }, "keywords": [ diff --git a/npm/scripts/uninstall.js b/npm/scripts/uninstall.js new file mode 100644 index 00000000..4350bf88 --- /dev/null +++ b/npm/scripts/uninstall.js @@ -0,0 +1,162 @@ +#!/usr/bin/env node +/** + * GitLink CLI npm 卸载脚本 + * npm preuninstall 钩子自动运行 + */ + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +// 颜色输出(支持跨平台) +const colors = { + green: '\x1b[32m', + yellow: '\x1b[33m', + red: '\x1b[31m', + cyan: '\x1b[36m', + blue: '\x1b[34m', + reset: '\x1b[0m' +}; + +function info(msg) { + console.log(`${colors.green}[INFO]${colors.reset} ${msg}`); +} + +function warn(msg) { + console.log(`${colors.yellow}[WARN]${colors.reset} ${msg}`); +} + +function error(msg) { + console.log(`${colors.red}[ERROR]${colors.reset} ${msg}`); +} + +function step(msg) { + console.log(`${colors.cyan}[STEP]${colors.reset} ${msg}`); +} + +// ---------- 删除文件/目录 ---------- +function removeSync(target) { + try { + if (fs.existsSync(target)) { + const stat = fs.statSync(target); + if (stat.isDirectory()) { + fs.rmdirSync(target, { recursive: true }); + info(`已删除目录: ${target}`); + } else { + fs.unlinkSync(target); + info(`已删除文件: ${target}`); + } + return true; + } + return false; + } catch (err) { + warn(`删除失败: ${target} - ${err.message}`); + return false; + } +} + +// ---------- 删除Skills ---------- +function removeSkills() { + step('删除 Skills...'); + + const skillsDir = path.join(os.homedir(), '.gitlink', 'skills'); + + if (!fs.existsSync(skillsDir)) { + warn('Skills目录不存在'); + return; + } + + // 统计skills数量 + let skillCount = 0; + try { + const items = fs.readdirSync(skillsDir); + skillCount = items.filter(item => { + const itemPath = path.join(skillsDir, item); + return fs.statSync(itemPath).isDirectory(); + }).length; + } catch (err) { + // 忽略错误 + } + + info(`找到 ${skillCount} 个Skills`); + + if (removeSync(skillsDir)) { + info('Skills已删除'); + } +} + +// ---------- 删除配置(可选)---------- +function removeConfig(purgeAll = false) { + if (!purgeAll) { + // npm卸载通常不删除配置 + info('保留配置文件(用户数据)'); + return; + } + + step('删除配置文件...'); + + const configPaths = [ + path.join(os.homedir(), '.gitlink-cli'), + path.join(os.homedir(), '.config', 'gitlink-cli'), + path.join(os.homedir(), '.gitlink') + ]; + + let removedCount = 0; + for (const configPath of configPaths) { + if (removeSync(configPath)) { + removedCount++; + } + } + + if (removedCount > 0) { + info('配置文件已删除'); + } else { + warn('未找到配置文件'); + } +} + +// ---------- 主流程 ---------- +function main() { + console.log(''); + console.log('========================================'); + info('GitLink CLI npm 卸载'); + console.log('========================================'); + console.log(''); + + // 检查环境变量 + const purgeAll = process.env.GITLINK_UNINSTALL_PURGE === 'true' || process.argv.includes('--purge'); + + step('开始清理npm安装的文件...'); + + // 删除skills + removeSkills(); + + // 删除配置(如果指定--purge) + removeConfig(purgeAll); + + console.log(''); + console.log('========================================'); + info('卸载完成!'); + console.log('========================================'); + console.log(''); + + if (!purgeAll) { + info('以下文件可能需要手动清理:'); + console.log(` - ${path.join(os.homedir(), '.gitlink-cli')}`); + console.log(` - ${path.join(os.homedir(), '.gitlink')}`); + console.log(''); + info('如需删除,请运行: npm uninstall -g @gitlink-ai/cli --purge'); + } + + console.log(''); + info('感谢使用 GitLink CLI!'); + console.log(''); +} + +// 运行 +try { + main(); +} catch (err) { + error(`卸载失败: ${err.message}`); + process.exit(1); +} \ No newline at end of file diff --git a/npm/scripts/update.js b/npm/scripts/update.js new file mode 100644 index 00000000..89a6e73f --- /dev/null +++ b/npm/scripts/update.js @@ -0,0 +1,429 @@ +#!/usr/bin/env node + +"use strict"; + +const os = require("os"); +const path = require("path"); +const fs = require("fs"); +const https = require("https"); +const http = require("http"); +const { execSync } = require("child_process"); +const PACKAGE = require("../package.json"); + +const VERSION = PACKAGE.version; +const BINARY_NAME = "gitlink-cli"; +const RELEASE_BASE = "https://www.gitlink.org.cn"; +const REPO_OWNER = "Gitlink"; +const REPO_NAME = "gitlink-cli"; + +// 颜色输出 +const colors = { + reset: "\x1b[0m", + green: "\x1b[32m", + yellow: "\x1b[33m", + red: "\x1b[31m", + cyan: "\x1b[36m", + blue: "\x1b[34m", +}; + +function info(msg) { + console.log(`${colors.green}[INFO]${colors.reset} ${msg}`); +} + +function warn(msg) { + console.log(`${colors.yellow}[WARN]${colors.reset} ${msg}`); +} + +function error(msg) { + console.log(`${colors.red}[ERROR]${colors.reset} ${msg}`); +} + +function step(msg) { + console.log(`${colors.cyan}[STEP]${colors.reset} ${msg}`); +} + +function debug(msg) { + if (process.env.DEBUG === "true") { + console.log(`${colors.blue}[DEBUG]${colors.reset} ${msg}`); + } +} + +function getPlatformInfo(platform = os.platform(), arch = os.arch()) { + const platformMap = { + darwin: "darwin", + linux: "linux", + win32: "windows", + }; + + const archMap = { + x64: "amd64", + arm64: "arm64", + }; + + const goPlatform = platformMap[platform]; + const goArch = archMap[arch]; + + if (!goPlatform || !goArch) { + throw new Error( + `Unsupported platform: ${platform}-${arch}. ` + + `Supported: darwin-x64, darwin-arm64, linux-x64, linux-arm64, win32-x64, win32-arm64` + ); + } + + return { platform: goPlatform, arch: goArch, isWindows: platform === "win32" }; +} + +function getBinaryName(platform) { + return platform === "windows" ? `${BINARY_NAME}.exe` : BINARY_NAME; +} + +function getArchiveName(platform, arch) { + const ext = platform === "windows" ? ".zip" : ".tar.gz"; + return `${BINARY_NAME}_${VERSION}_${platform}_${arch}${ext}`; +} + +function fetch(url, options = {}) { + return new Promise((resolve, reject) => { + const maxRedirects = options.maxRedirects || 5; + let redirectCount = 0; + + function doRequest(currentUrl) { + const mod = currentUrl.startsWith("https") ? https : http; + const req = mod.get(currentUrl, (res) => { + // Follow redirects + if ( + (res.statusCode === 301 || + res.statusCode === 302 || + res.statusCode === 307 || + res.statusCode === 308) && + res.headers.location + ) { + redirectCount++; + if (redirectCount > maxRedirects) { + reject(new Error(`Too many redirects (max ${maxRedirects})`)); + return; + } + let redirectUrl = res.headers.location; + if (redirectUrl.startsWith("/")) { + const parsed = new URL(currentUrl); + redirectUrl = `${parsed.protocol}//${parsed.host}${redirectUrl}`; + } + doRequest(redirectUrl); + return; + } + + if (res.statusCode !== 200) { + reject(new Error(`HTTP ${res.statusCode} when downloading ${currentUrl}`)); + return; + } + + if (options.json) { + let body = ""; + res.on("data", (chunk) => (body += chunk)); + res.on("end", () => { + try { + resolve(JSON.parse(body)); + } catch (e) { + reject(e); + } + }); + } else { + res.pipe(resolve); + } + }); + + req.on("error", reject); + req.setTimeout(options.timeout || 30000, () => { + req.destroy(); + reject(new Error(`Request timeout: ${currentUrl}`)); + }); + } + + doRequest(url); + }); +} + +// 下载文件(带重试) +async function downloadFile(url, outputPath, maxAttempts = 3) { + let attempt = 1; + + while (attempt <= maxAttempts) { + try { + step(`下载 (尝试 ${attempt}/${maxAttempts}): ${path.basename(url)}`); + debug(`URL: ${url}`); + + await new Promise((resolve, reject) => { + const file = fs.createWriteStream(outputPath); + const mod = url.startsWith("https") ? https : http; + + const req = mod.get(url, (res) => { + if (res.statusCode !== 200) { + reject(new Error(`HTTP ${res.statusCode}`)); + return; + } + + const totalSize = parseInt(res.headers["content-length"], 10); + let downloadedSize = 0; + + res.on("data", (chunk) => { + downloadedSize += chunk.length; + if (totalSize) { + const progress = ((downloadedSize / totalSize) * 100).toFixed(1); + process.stdout.write(`\r下载进度: ${progress}%`); + } + }); + + res.pipe(file); + + file.on("finish", () => { + file.close(); + process.stdout.write("\r"); + resolve(); + }); + + file.on("error", (err) => { + fs.unlink(outputPath, () => {}); + reject(err); + }); + }); + + req.on("error", (err) => { + file.destroy(); + fs.unlink(outputPath, () => {}); + reject(err); + }); + + req.setTimeout(120000, () => { + req.destroy(); + file.destroy(); + fs.unlink(outputPath, () => {}); + reject(new Error("下载超时")); + }); + }); + + if (fs.existsSync(outputPath) && fs.statSync(outputPath).size > 0) { + const sizeMB = (fs.statSync(outputPath).size / (1024 * 1024)).toFixed(2); + info(`下载成功: ${path.basename(outputPath)} (${sizeMB}MB)`); + return true; + } else { + warn("下载文件为空"); + } + } catch (err) { + warn(`下载失败: ${err.message}`); + } + + if (attempt < maxAttempts) { + const waitTime = attempt * 2; + warn(`等待 ${waitTime}s 后重试...`); + await new Promise((resolve) => setTimeout(resolve, waitTime * 1000)); + } + + attempt++; + } + + error("下载失败,已尝试 ${maxAttempts} 次"); + return false; +} + +// 获取最新版本 +async function getLatestVersion() { + try { + step("检查更新..."); + const releasesUrl = `${RELEASE_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases.json`; + const releases = await fetch(releasesUrl, { json: true, timeout: 30000 }); + + if (releases && releases.length > 0) { + return releases[0].tag_name; + } + } catch (err) { + debug(`获取版本失败: ${err.message}`); + } + + return null; +} + +// 检查是否有更新 +async function checkForUpdate() { + try { + const latestVersion = await getLatestVersion(); + + if (!latestVersion) { + info("无法获取最新版本信息"); + return; + } + + const currentVersion = VERSION.startsWith("v") ? VERSION : `v${VERSION}`; + const latest = latestVersion.startsWith("v") ? latestVersion : `v${latestVersion}`; + + info(`当前版本: ${currentVersion}`); + info(`最新版本: ${latest}`); + + if (currentVersion === latest) { + info("已经是最新版本"); + return; + } + + // 简单的版本比较 + if (latest > currentVersion) { + warn(`发现新版本: ${latest}`); + warn("运行 'npm update -g @gitlink-ai/cli' 更新"); + } else if (latest < currentVersion) { + info("当前版本比最新发布版本更新(开发版本)"); + } + } catch (err) { + debug(`检查更新失败: ${err.message}`); + } +} + +// 安装二进制 +async function installBinary() { + const platform = getPlatformInfo(); + info(`平台: ${platform.platform}-${platform.arch}`); + + const binaryName = getBinaryName(platform.platform); + const archiveName = getArchiveName(platform.platform, platform.arch); + const npmBinDir = path.dirname(process.execPath); + const installDir = path.join(npmBinDir, ".."); + + step(`安装二进制到: ${installDir}`); + + const version = VERSION.startsWith("v") ? VERSION : `v${VERSION}`; + const binaryUrl = `${RELEASE_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases/${version}/assets/${archiveName}`; + + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gitlink-cli-")); + const archivePath = path.join(tmpDir, archiveName); + + try { + // 下载 + const success = await downloadFile(binaryUrl, archivePath); + if (!success) { + throw new Error("下载失败"); + } + + // 解压 + step("解压..."); + if (platform.isWindows) { + const AdmZip = require("adm-zip"); + const zip = new AdmZip(archivePath); + zip.extractAllTo(installDir, true); + } else { + const tar = require("tar"); + await tar.x({ + file: archivePath, + cwd: installDir, + strip: 1, + }); + } + + // 设置执行权限(Unix) + if (!platform.isWindows) { + const binaryPath = path.join(installDir, binaryName); + if (fs.existsSync(binaryPath)) { + fs.chmodSync(binaryPath, "755"); + } + } + + info(`二进制安装成功: ${binaryName}`); + } finally { + // 清理临时文件 + fs.rmSync(tmpDir, { recursive: true, force: true }); + } +} + +// 安装skills +async function installSkills() { + const version = VERSION.startsWith("v") ? VERSION : `v${VERSION}`; + const skillsArchive = `${BINARY_NAME}_${version}_skills.zip`; + const skillsUrl = `${RELEASE_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases/${version}/assets/${skillsArchive}`; + + const skillsDir = path.join(os.homedir(), ".gitlink", "skills"); + + if (!fs.existsSync(skillsDir)) { + fs.mkdirSync(skillsDir, { recursive: true }); + } + + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gitlink-skills-")); + const archivePath = path.join(tmpDir, skillsArchive); + + try { + step("安装 skills..."); + + const success = await downloadFile(skillsUrl, archivePath); + if (!success) { + warn("Skills 包下载失败(可稍后手动安装)"); + return; + } + + // 解压 + const AdmZip = require("adm-zip"); + const zip = new AdmZip(archivePath); + zip.extractAllTo(skillsDir, true); + + info("Skills 安装成功"); + } catch (err) { + warn(`Skills 安装失败: ${err.message}`); + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } +} + +// 验证安装 +function verifyInstallation() { + step("验证安装..."; + + try { + const result = execSync("gitlink-cli version", { encoding: "utf8" }); + info(`安装成功! ${result.trim()}`); + return true; + } catch (err) { + warn("验证命令失败(可能需要重启终端)"); + return false; + } +} + +// 主函数 +async function main() { + console.log(""); + console.log(" ╔══════════════════════════════════════╗"); + console.log(" ║ GitLink CLI npm 安装脚本 ║"); + console.log(" ╚══════════════════════════════════════╝"); + console.log(""); + + try { + await installBinary(); + await installSkills(); + verifyInstallation(); + + console.log(""); + info("快速开始:"); + info(" gitlink-cli auth login # 登录账号"); + info(" gitlink-cli --help # 查看所有命令"); + info(" gitlink-cli version # 查看版本信息"); + console.log(""); + } catch (err) { + error(`安装失败: ${err.message}`); + process.exit(1); + } +} + +// 如果直接运行此脚本 +if (require.main === module || process.argv[1].endsWith("update.js")) { + // 检查更新 + if (process.argv.includes("--check")) { + (async () => { + await checkForUpdate(); + })(); + } else { + // 安装 + (async () => { + await main(); + })(); + } +} + +module.exports = { + main, + checkForUpdate, + installBinary, + installSkills, +}; diff --git a/uninstall.ps1 b/uninstall.ps1 new file mode 100644 index 00000000..2c4fc7d6 --- /dev/null +++ b/uninstall.ps1 @@ -0,0 +1,328 @@ +# GitLink CLI Windows 卸载脚本 +# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File uninstall.ps1 + +param( + [switch]$Purge, + [switch]$Yes, + [switch]$Help +) + +$ErrorActionPreference = "Stop" + +# ---------- 工具函数 ---------- +function Info-Message { + param([string]$Message) + Write-Host "[INFO] $Message" -ForegroundColor Green +} + +function Warn-Message { + param([string]$Message) + Write-Host "[WARN] $Message" -ForegroundColor Yellow +} + +function Error-Message { + param([string]$Message) + Write-Host "[ERROR] $Message" -ForegroundColor Red +} + +function Step-Message { + param([string]$Message) + Write-Host "[STEP] $Message" -ForegroundColor Cyan +} + +function Ask-Question { + param([string]$Question) + Write-Host "[ASK] $Question" -ForegroundColor Blue +} + +# ---------- 显示帮助 ---------- +if ($Help) { + Write-Host "" + Write-Host "GitLink CLI Windows 卸载脚本" + Write-Host "" + Write-Host "用法: powershell -NoProfile -ExecutionPolicy Bypass -File uninstall.ps1 [选项]" + Write-Host "" + Write-Host "选项:" + Write-Host " -Purge 删除所有文件(包括配置)" + Write-Host " -Yes 自动确认,不询问" + Write-Host " -Help 显示此帮助" + Write-Host "" + Write-Host "示例:" + Write-Host " .\uninstall.ps1 # 交互式卸载" + Write-Host " .\uninstall.ps1 -Purge # 完全删除" + Write-Host " .\uninstall.ps1 -Yes # 自动确认" + exit 0 +} + +# ---------- 查找二进制文件 ---------- +function Find-Binary { + Step-Message "查找 gitlink-cli 二进制文件..." + + $possibleDirs = @( + "$env:USERPROFILE\.gitlink-cli\bin", + "$env:USERPROFILE\.local\bin", + "$env:USERPROFILE\scoop\shims", + "$env:ChocolateyInstall\bin", + "$env:USERPROFILE\go\bin" + ) + + foreach ($dir in $possibleDirs) { + $binaryPath = Join-Path $dir "gitlink-cli.exe" + if (Test-Path $binaryPath) { + $script:InstallDir = $dir + Info-Message "找到安装目录: $dir" + return $true + } + } + + # 检查PATH + if (Get-Command "gitlink-cli" -ErrorAction SilentlyContinue) { + $script:InstallDir = Split-Path (Get-Command "gitlink-cli").Source -Parent + Info-Message "通过PATH找到安装目录: $script:InstallDir" + return $true + } + + Warn-Message "未找到 gitlink-cli 二进制文件" + return $false +} + +# ---------- 检查安装状态 ---------- +function Test-InstallationStatus { + Step-Message "检查安装状态..." + + $foundItems = @() + + if ($script:InstallDir -and (Test-Path (Join-Path $script:InstallDir "gitlink-cli.exe"))) { + $foundItems += "二进制文件: $($script:InstallDir)\gitlink-cli.exe" + } + + if (Test-Path "$env:USERPROFILE\.gitlink\skills") { + $foundItems += "Skills: $env:USERPROFILE\.gitlink\skills" + } + + if (Test-Path "$env:USERPROFILE\.gitlink-cli") { + $foundItems += "配置: $env:USERPROFILE\.gitlink-cli" + } + + if (Test-Path "$env:USERPROFILE\.gitlink") { + $foundItems += "GitLink数据: $env:USERPROFILE\.gitlink" + } + + # 检查npm + try { + $null = npm list -g @gitlink-ai/cli 2>&1 | Select-String "@gitlink-ai/cli" + if ($?) { + $foundItems += "npm包: @gitlink-ai/cli (全局)" + } + } catch { + # npm未安装或包未安装 + } + + if ($foundItems.Count -eq 0) { + Warn-Message "未检测到 gitlink-cli 安装" + return $false + } + + Info-Message "检测到以下组件:" + foreach ($item in $foundItems) { + Write-Host " - $item" + } + + return $true +} + +# ---------- 删除二进制 ---------- +function Remove-Binary { + if (-not $script:InstallDir) { + Warn-Message "跳过二进制删除(未找到安装目录)" + return + } + + Step-Message "删除二进制文件..." + $binaryPath = Join-Path $script:InstallDir "gitlink-cli.exe" + + if (-not (Test-Path $binaryPath)) { + Warn-Message "二进制文件不存在: $binaryPath" + return + } + + try { + Remove-Item $binaryPath -Force + Info-Message "已删除: $binaryPath" + } catch { + Error-Message "删除失败: $_" + Error-Message "请手动删除: $binaryPath" + throw + } +} + +# ---------- 删除Skills ---------- +function Remove-Skills { + Step-Message "删除 Skills..." + + $skillsPath = "$env:USERPROFILE\.gitlink\skills" + if (-not (Test-Path $skillsPath)) { + Warn-Message "Skills目录不存在: $skillsPath" + return + } + + try { + $skillCount = (Get-ChildItem $skillsPath -Directory).Count + Info-Message "找到 $skillCount 个Skills" + + Remove-Item $skillsPath -Recurse -Force + Info-Message "已删除Skills" + } catch { + Error-Message "删除Skills失败: $_" + } +} + +# ---------- 删除配置 ---------- +function Remove-Config { + if ($Purge) { + Step-Message "删除配置文件(--purge模式)..." + } else { + if (-not $Yes) { + Ask-Question "是否删除配置文件和数据? [y/N] " + $response = Read-Host + if ($response -ne 'y' -and $response -ne 'Y') { + Info-Message "保留配置文件" + return + } + } + Step-Message "删除配置文件..." + } + + $configPaths = @( + "$env:USERPROFILE\.gitlink-cli", + "$env:USERPROFILE\.gitlink" + ) + + foreach ($path in $configPaths) { + if (Test-Path $path) { + try { + Info-Message "删除: $path" + Remove-Item $path -Recurse -Force + } catch { + Warn-Message "删除失败 $path : $_" + } + } + } + + Info-Message "配置文件已删除" +} + +# ---------- 卸载npm包 ---------- +function Uninstall-Npm { + try { + $null = npm list -g @gitlink-ai/cli 2>&1 | Select-String "@gitlink-ai/cli" + if (-not $?) { + return + } + } catch { + return + } + + Step-Message "卸载npm包..." + + if ($Yes) { + $response = 'y' + } else { + Ask-Question "检测到npm全局安装的 gitlink-cli,是否卸载? [y/N] " + $response = Read-Host + } + + if ($response -eq 'y' -or $response -eq 'Y') { + try { + npm uninstall -g @gitlink-ai/cli | Out-Null + Info-Message "npm包已卸载" + } catch { + Warn-Message "npm包卸载失败,请手动执行: npm uninstall -g @gitlink-ai/cli" + } + } else { + Info-Message "保留npm包" + } +} + +# ---------- 显示卸载摘要 ---------- +function Show-Summary { + Write-Host "" + Write-Host "========================================" + Info-Message "卸载完成!" + Write-Host "========================================" + Write-Host "" + + if ($Purge) { + Warn-Message "已完全删除所有文件(包括配置)" + } else { + Info-Message "以下文件可能需要手动清理:" + Write-Host " - $env:USERPROFILE\.gitlink-cli" + Write-Host " - $env:USERPROFILE\.gitlink" + Write-Host "" + Info-Message "如需删除,请重新运行: .\uninstall.ps1 -Purge" + } + + Write-Host "" + Info-Message "感谢使用 GitLink CLI!" + Info-Message "如有任何问题,请访问: https://www.gitlink.org.cn/Gitlink/gitlink-cli/issues" + Write-Host "" +} + +# ---------- 确认卸载 ---------- +function Confirm-Uninstall { + if ($Yes) { + return + } + + Write-Host "" + Warn-Message "即将卸载 gitlink-cli 及相关文件" + Ask-Question "确认继续? [y/N] " + $response = Read-Host + + if ($response -ne 'y' -and $response -ne 'Y') { + Info-Message "取消卸载" + exit 0 + } +} + +# ---------- 主流程 ---------- +function Main { + # 欢迎信息 + Write-Host "" + Write-Host "========================================" + Write-Host " GitLink CLI 一键卸载 (Windows)" + Write-Host "========================================" + Write-Host "" + + # 查找二进制 + if (-not (Find-Binary)) { + Error-Message "未检测到 gitlink-cli 安装" + exit 1 + } + + # 检查安装状态 + if (-not (Test-InstallationStatus)) { + Error-Message "未检测到 gitlink-cli 安装" + exit 1 + } + + # 确认卸载 + Confirm-Uninstall + + # 执行卸载 + Remove-Binary + Remove-Skills + Remove-Config + Uninstall-Npm + + # 显示摘要 + Show-Summary +} + +# 运行主流程 +try { + Main +} catch { + Error-Message "卸载失败: $_" + exit 1 +} \ No newline at end of file diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 00000000..f1caba02 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,317 @@ +#!/bin/bash +# GitLink CLI 一键卸载脚本 +# 删除二进制、skills和配置文件 +# 用法: curl -sSL https://www.gitlink.org.cn/Gitlink/gitlink-cli/raw/master/uninstall.sh | bash +# 或者: bash uninstall.sh [--purge] + +set -e + +REPO_OWNER="Gitlink" +REPO_NAME="gitlink-cli" +BINARY_NAME="gitlink-cli" +API_BASE="https://www.gitlink.org.cn" + +# 默认安装目录(按优先级排序) +POSSIBLE_INSTALL_DIRS=( + "/usr/local/bin" + "/usr/bin" + "$HOME/.local/bin" + "$HOME/bin" + "/opt/homebrew/bin" + "$HOME/.gitlink-cli/bin" +) + +# ---------- 颜色输出 ---------- +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} $*"; } +ask() { echo -e "${BLUE}[ASK]${NC} $*"; } + +# ---------- 查找二进制文件 ---------- +find_binary() { + step "查找 ${BINARY_NAME} 二进制文件..." + + for dir in "${POSSIBLE_INSTALL_DIRS[@]}"; do + if [ -f "${dir}/${BINARY_NAME}" ]; then + INSTALL_DIR="${dir}" + info "找到安装目录: ${INSTALL_DIR}" + return 0 + fi + done + + # 检查是否在PATH中 + if command -v "${BINARY_NAME}" >/dev/null 2>&1; then + INSTALL_DIR="$(dirname "$(command -v "${BINARY_NAME}")")" + info "通过PATH找到安装目录: ${INSTALL_DIR}" + return 0 + fi + + warn "未找到 ${BINARY_NAME} 二进制文件" + return 1 +} + +# ---------- 检查安装状态 ---------- +check_installation_status() { + local found_items=() + + step "检查安装状态..." + + # 检查二进制 + if [ -n "${INSTALL_DIR}" ] && [ -f "${INSTALL_DIR}/${BINARY_NAME}" ]; then + found_items+=("二进制文件: ${INSTALL_DIR}/${BINARY_NAME}") + fi + + # 检查skills + if [ -d "${HOME}/.gitlink/skills" ]; then + found_items+=("Skills: ${HOME}/.gitlink/skills") + fi + + # 检查配置 + if [ -d "${HOME}/.config/gitlink-cli" ]; then + found_items+=("配置: ${HOME}/.config/gitlink-cli") + fi + + if [ -d "${HOME}/.gitlink" ]; then + found_items+=("GitLink数据: ${HOME}/.gitlink") + fi + + # 检查npm安装 + if npm list -g "@gitlink-ai/cli" >/dev/null 2>&1; then + found_items+=("npm包: @gitlink-ai/cli (全局)") + fi + + if [ ${#found_items[@]} -eq 0 ]; then + warn "未检测到 ${BINARY_NAME} 安装" + return 1 + fi + + info "检测到以下组件:" + for item in "${found_items[@]}"; do + echo " - ${item}" + done + + return 0 +} + +# ---------- 删除二进制 ---------- +remove_binary() { + if [ -z "${INSTALL_DIR}" ]; then + warn "跳过二进制删除(未找到安装目录)" + return 0 + fi + + step "删除二进制文件..." + local binary_path="${INSTALL_DIR}/${BINARY_NAME}" + + if [ ! -f "${binary_path}" ]; then + warn "二进制文件不存在: ${binary_path}" + return 0 + fi + + # 尝试删除 + if [ -w "${binary_path}" ]; then + rm -f "${binary_path}" + info "已删除: ${binary_path}" + else + if [ -w "${INSTALL_DIR}" ]; then + rm -f "${binary_path}" + info "已删除: ${binary_path}" + else + # 需要sudo权限 + warn "需要管理员权限删除二进制文件" + if sudo rm -f "${binary_path}" 2>/dev/null; then + info "已删除: ${binary_path}" + else + error "删除失败,请手动删除: ${binary_path}" + return 1 + fi + fi + fi +} + +# ---------- 删除Skills ---------- +remove_skills() { + step "删除 Skills..." + + if [ ! -d "${HOME}/.gitlink/skills" ]; then + warn "Skills目录不存在: ${HOME}/.gitlink/skills" + return 0 + fi + + # 显示Skills信息 + local skill_count=$(find "${HOME}/.gitlink/skills" -maxdepth 1 -type d | wc -l) + info "找到 ${skill_count} 个Skills" + + # 删除 + rm -rf "${HOME}/.gitlink/skills" + info "已删除Skills" +} + +# ---------- 删除配置 ---------- +remove_config() { + if [ "$PURGE_ALL" != "true" ]; then + ask "是否删除配置文件和数据? [y/N] " + read -r response + if [[ ! "$response" =~ ^[Yy]$ ]]; then + info "保留配置文件" + return 0 + fi + fi + + step "删除配置文件..." + + local config_dirs=( + "$HOME/.config/gitlink-cli" + "$HOME/.gitlink" + ) + + for dir in "${config_dirs[@]}"; do + if [ -d "$dir" ]; then + info "删除: $dir" + rm -rf "$dir" + fi + done + + info "配置文件已删除" +} + +# ---------- 卸载npm包 ---------- +uninstall_npm() { + if ! npm list -g "@gitlink-ai/cli" >/dev/null 2>&1; then + return 0 + fi + + step "卸载npm包..." + + ask "检测到npm全局安装的 ${BINARY_NAME},是否卸载? [y/N] " + read -r response + + if [[ "$response" =~ ^[Yy]$ ]]; then + if npm uninstall -g "@gitlink-ai/cli" 2>/dev/null; then + info "npm包已卸载" + else + warn "npm包卸载失败,请手动执行: npm uninstall -g @gitlink-ai/cli" + fi + else + info "保留npm包" + fi +} + +# ---------- 显示卸载摘要 ---------- +show_summary() { + echo "" + echo "========================================" + info "卸载完成!" + echo "========================================" + echo "" + + if [ "$PURGE_ALL" = "true" ]; then + warn "已完全删除所有文件(包括配置)" + else + info "以下文件可能需要手动清理:" + echo " - ${HOME}/.config/gitlink-cli" + echo " - ${HOME}/.gitlink" + echo "" + info "如需删除,请重新运行: bash uninstall.sh --purge" + fi + + echo "" + info "感谢使用 GitLink CLI!" + echo "如有任何问题,请访问: https://www.gitlink.org.cn/Gitlink/gitlink-cli/issues" + echo "" +} + +# ---------- 确认卸载 ---------- +confirm_uninstall() { + if [ "$AUTO_CONFIRM" = "true" ]; then + return 0 + fi + + echo "" + warn "即将卸载 ${BINARY_NAME} 及相关文件" + ask "确认继续? [y/N] " + read -r response + + if [[ ! "$response" =~ ^[Yy]$ ]]; then + info "取消卸载" + exit 0 + fi +} + +# ---------- 主流程 ---------- +main() { + # 解析参数 + PURGE_ALL="false" + AUTO_CONFIRM="false" + + while [[ $# -gt 0 ]]; do + case $1 in + --purge) + PURGE_ALL="true" + shift + ;; + --yes|-y) + AUTO_CONFIRM="true" + shift + ;; + --help|-h) + echo "用法: bash uninstall.sh [选项]" + echo "" + echo "选项:" + echo " --purge 删除所有文件(包括配置)" + echo " --yes, -y 自动确认,不询问" + echo " --help, -h 显示此帮助" + echo "" + echo "示例:" + echo " bash uninstall.sh # 交互式卸载" + echo " bash uninstall.sh --purge # 完全删除" + echo " bash uninstall.sh -y # 自动确认" + exit 0 + ;; + *) + error "未知选项: $1" + echo "使用 --help 查看帮助" + exit 1 + ;; + esac + done + + # 欢迎信息 + echo "" + echo "========================================" + echo " GitLink CLI 一键卸载" + echo "========================================" + echo "" + + # 查找二进制 + find_binary + + # 检查安装状态 + if ! check_installation_status; then + error "未检测到 ${BINARY_NAME} 安装" + exit 1 + fi + + # 确认卸载 + confirm_uninstall + + # 执行卸载 + remove_binary + remove_skills + remove_config + uninstall_npm + + # 显示摘要 + show_summary +} + +# 运行主流程 +main "$@" \ No newline at end of file