commit be7720e732060b607d41d6e5aac731001a59d344 Author: wonder Date: Mon Aug 31 10:30:53 2026 +0800 feat: 初始化 skills 仓库,添加 wiki 和 todo 两个 skill - 添加 wiki skill:文档站管理,支持向 MkDocs 文档站添加/更新技术文档 - SSH 连通性检查脚本 - 文章模板参考(标准格式:摘要→核心概念→详解→代码→陷阱→练习题) - mkdocs.yml 导航结构参考 - 添加 todo skill:任务待办管理,通过自然语言操作 MySQL 待办事项 - LRU 淘汰策略(基于 last_accessed_at) - MySQL MCP 配置指南及建表 SQL - 自然语言到 SQL 的映射规则 - 添加 README.md 说明文档 diff --git a/README.md b/README.md new file mode 100644 index 0000000..60ed1e3 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# my-skills + +个人 Claude Code Skills 集合。 + +## Skills + +| Skill | 命令 | 说明 | +|-------|------|------| +| [wiki](wiki/) | `/wiki` | 文档站管理,向 MkDocs 文档站添加/更新技术文档 | +| [todo](todo/) | `/todo` | 任务待办管理,通过自然语言操作 MySQL 中的待办事项 | + +## 安装 + +将 skill 目录复制或软链接到 `~/.claude/skills/` 即可。 + +```bash +# 方式一:软链接(推荐,方便更新) +ln -sf /path/to/my-skills/wiki ~/.claude/skills/wiki +ln -sf /path/to/my-skills/todo ~/.claude/skills/todo + +# 方式二:直接复制 +cp -r wiki ~/.claude/skills/ +cp -r todo ~/.claude/skills/ +``` diff --git a/todo/SKILL.md b/todo/SKILL.md new file mode 100644 index 0000000..e82515f --- /dev/null +++ b/todo/SKILL.md @@ -0,0 +1,176 @@ +--- +name: todo +description: | + 任务待办管理 skill。通过自然语言操作 MySQL 数据库中的待办事项。 + 当用户提到待办、任务管理、todo list、添加任务、查看任务、标记完成、 + 清理任务、归档旧任务时,使用此 skill。 + 支持增删改查和基于 LRU 策略的自动归档。 +--- + +# 任务待办管理 + +通过自然语言管理 MySQL 数据库中的待办事项,支持 LRU 自动淘汰。 + +## 前置条件 + +此 skill 依赖 MySQL MCP 工具。使用前需确认: + +1. MySQL MCP 已配置(参考 `references/setup.md`) +2. todos 表已创建(参考 `references/setup.md` 中的建表 SQL) + +如果 MCP 工具不可用,引导用户参考 `references/setup.md` 完成配置。 + +## 自然语言操作映射 + +### 添加任务 + +用户说:"添加任务:XXX" / "新建一个待办:XXX" / "提醒我做 XXX" + +```sql +INSERT INTO todos (title, description, priority) VALUES ('<标题>', '<描述>', '<优先级>'); +``` + +- 优先级默认 `medium`,如果用户提到"紧急/urgent"则为 `urgent`,"重要"为 `high` +- 添加后自动执行 LRU 检查(见淘汰策略) + +### 查看待办 + +用户说:"我的待办" / "有什么任务" / "看看今天的任务" + +```sql +SELECT id, title, status, priority, last_accessed_at +FROM todos +WHERE status IN ('pending', 'in_progress') +ORDER BY + CASE priority + WHEN 'urgent' THEN 0 + WHEN 'high' THEN 1 + WHEN 'medium' THEN 2 + WHEN 'low' THEN 3 + END, + last_accessed_at DESC; +``` + +查看后刷新访问时间: + +```sql +UPDATE todos SET last_accessed_at = NOW(), access_count = access_count + 1 +WHERE id IN (<查看到的 id 列表>); +``` + +### 查看已完成 + +用户说:"已完成的任务" / "看看做完了哪些" + +```sql +SELECT id, title, priority, updated_at +FROM todos WHERE status = 'done' +ORDER BY updated_at DESC; +``` + +### 标记完成 + +用户说:"完成 XXX" / "把这个标记为完成" / "做完了" + +```sql +UPDATE todos SET status = 'done', last_accessed_at = NOW(), access_count = access_count + 1 +WHERE id = OR title LIKE '%<关键词>%'; +``` + +如果匹配多条,列出匹配项让用户确认。 + +### 标记进行中 + +用户说:"开始做 XXX" / "进行中" + +```sql +UPDATE todos SET status = 'in_progress', last_accessed_at = NOW(), access_count = access_count + 1 +WHERE id = ; +``` + +### 更新任务 + +用户说:"修改 XXX 的描述为 YYY" / "把 XXX 的优先级改为 high" + +```sql +UPDATE todos SET <字段> = '<新值>', last_accessed_at = NOW(), access_count = access_count + 1 +WHERE id = ; +``` + +### 删除任务 + +用户说:"删除任务 XXX" / "去掉这个待办" + +**软删除(推荐)**: + +```sql +UPDATE todos SET status = 'archived' WHERE id = ; +``` + +**硬删除**(用户明确要求彻底删除时): + +```sql +DELETE FROM todos WHERE id = ; +``` + +- 硬删除前必须确认,列出将被删除的事项 +- 默认使用软删除(归档),保护数据 + +### 搜索任务 + +用户说:"找一下关于 XXX 的任务" + +```sql +SELECT id, title, description, status, priority +FROM todos +WHERE title LIKE '%<关键词>%' OR description LIKE '%<关键词>%' +ORDER BY status, priority; +``` + +## LRU 淘汰策略 + +### 自动触发 + +每次 INSERT 后检查: + +```sql +SELECT COUNT(*) FROM todos WHERE status != 'archived'; +``` + +如果总数超过 100,执行归档。 + +### 归档逻辑 + +1. **优先归档已完成**:归档最久未访问的 `done` 事项(取最旧的 10 条) +2. **其次归档待办**:如果仍超标,归档最久未访问的 `pending` 事项 +3. **保护进行中**:不归档 `in_progress` 状态的事项 +4. **告知用户**:归档时列出被归档的事项标题 + +### 手动触发 + +用户说:"清理旧任务" / "归档一些任务" / "整理一下待办" + +执行同样的归档逻辑,但需要先告知用户当前待办数量,并询问是否确认清理。 + +## 交互原则 + +- **模糊匹配时确认**:如果用户说的关键词匹配多条任务,列出选项让用户确认 +- **操作前预览**:删除/归档操作前,显示受影响的事项列表 +- **优先级合理推断**:用户说"紧急""马上""ASAP" → urgent;"重要" → high;默认 → medium +- **自然语言理解**:用户可能说"帮我记一下明天要买菜",理解为添加任务"买菜" +- **状态转换合理**:不要让用户手动做不合理的状态跳转(如 archived → in_progress) + +## 输出格式 + +查询结果以表格形式展示: + +``` +| ID | 标题 | 状态 | 优先级 | 更新时间 | +|----|------|------|--------|----------| +| 1 | xxx | 待办 | 高 | 08-31 | +``` + +操作结果用简洁的确认语句: +- ✓ 已添加任务:XXX +- ✓ 已完成任务:XXX +- ✓ 已归档 5 条旧任务 diff --git a/todo/references/setup.md b/todo/references/setup.md new file mode 100644 index 0000000..a83880f --- /dev/null +++ b/todo/references/setup.md @@ -0,0 +1,112 @@ +# Todo Skill 环境配置指南 + +## 1. 安装 MySQL MCP Server + +推荐使用 `@benborla29/mcp-server-mysql`(支持读写操作)。 + +### 配置方式 + +在项目的 `.mcp.json` 文件中添加(或在 `~/.claude.json` 中配置): + +```json +{ + "mcpServers": { + "mysql": { + "command": "npx", + "args": ["-y", "@benborla29/mcp-server-mysql"], + "env": { + "MYSQL_HOST": "<替换为你的主机地址>", + "MYSQL_PORT": "<替换为你的端口,默认 3306>", + "MYSQL_USER": "<替换为你的用户名>", + "MYSQL_PASSWORD": "<替换为你的密码>", + "MYSQL_DATABASE": "<替换为你的数据库名>" + } + } + } +} +``` + +### 验证 MCP 可用 + +配置完成后重启 Claude Code,然后尝试: +- 列出数据库中的表 +- 执行一条简单查询 + +如果 MCP 工具可用,会看到 `mysql` 相关的工具。 + +--- + +## 2. 创建 todos 表 + +在目标数据库中执行以下 SQL: + +```sql +CREATE TABLE IF NOT EXISTS todos ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(500) NOT NULL COMMENT '任务标题', + description TEXT COMMENT '任务描述', + status ENUM('pending', 'in_progress', 'done', 'archived') NOT NULL DEFAULT 'pending' COMMENT '状态: pending-待办, in_progress-进行中, done-已完成, archived-已归档', + priority ENUM('low', 'medium', 'high', 'urgent') NOT NULL DEFAULT 'medium' COMMENT '优先级: low-低, medium-中, high-高, urgent-紧急', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间', + last_accessed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后访问时间(LRU 淘汰依据)', + access_count INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '访问次数', + INDEX idx_status (status), + INDEX idx_lru (last_accessed_at), + INDEX idx_priority_status (priority, status), + INDEX idx_created (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='待办事项表,支持 LRU 淘汰策略'; +``` + +### 字段说明 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT UNSIGNED | 自增主键 | +| title | VARCHAR(500) | 任务标题,必填 | +| description | TEXT | 任务描述,可选 | +| status | ENUM | 状态:pending / in_progress / done / archived | +| priority | ENUM | 优先级:low / medium / high / urgent | +| created_at | DATETIME | 创建时间,自动填充 | +| updated_at | DATETIME | 更新时间,自动刷新 | +| last_accessed_at | DATETIME | 最后访问时间,每次查看/更新时刷新 | +| access_count | INT UNSIGNED | 访问计数,每次访问 +1 | + +--- + +## 3. LRU 淘汰策略 + +### 原理 + +基于 `last_accessed_at` 字段实现 LRU(Least Recently Used)淘汰: + +- 每次查看、更新 todo 时,刷新 `last_accessed_at` 为当前时间,`access_count` +1 +- 当待办事项总数超过阈值(默认 100 条非 archived 记录)时,自动触发归档 +- 归档优先级:先归档最久未访问的 `done` 事项,再归档最久未访问的 `pending` 事项 + +### 归档 SQL + +```sql +-- 查询非 archived 的事项总数 +SELECT COUNT(*) FROM todos WHERE status != 'archived'; + +-- 归档最久未访问的已完成事项(取最旧的 10 条) +UPDATE todos +SET status = 'archived' +WHERE status = 'done' +ORDER BY last_accessed_at ASC +LIMIT 10; + +-- 如果仍然超标,归档最久未访问的待办事项 +UPDATE todos +SET status = 'archived' +WHERE status = 'pending' +ORDER BY last_accessed_at ASC +LIMIT 10; +``` + +### 触发时机 + +- 每次 INSERT 新事项后,检查总数是否超标 +- 用户主动要求"清理"或"归档旧任务"时 +- 不会自动归档 `in_progress` 状态的事项(保护正在做的任务) diff --git a/wiki/SKILL.md b/wiki/SKILL.md new file mode 100644 index 0000000..92829d2 --- /dev/null +++ b/wiki/SKILL.md @@ -0,0 +1,103 @@ +--- +name: wiki +description: | + 文档站管理 skill。用于向个人 MkDocs 文档站添加、更新或整理技术文档。 + 当用户提到写文档、添加到文档站、整理笔记到 wiki、记录技术知识点、 + 或者想把某段内容发布到文档站时,使用此 skill。 + 支持用户提供主题(自动生成文档)或提供已有内容(整理后发布)。 +--- + +# 文档站管理 + +管理 MkDocs Material 文档站,支持添加和更新技术文档。 + +## 工作流程 + +### 1. SSH 连通性检查 + +在操作仓库之前,先运行 SSH 检查脚本: + +```bash +bash /scripts/check_ssh.sh +``` + +如果检查失败,告知用户需要配置 SSH 密钥,不要继续操作。 + +### 2. 克隆仓库 + +```bash +TMP_DIR=$(mktemp -d) +git clone ssh://git@47.121.181.112:222/wonder/docs.git "$TMP_DIR" +``` + +后续所有文件操作在 `$TMP_DIR` 中进行。 + +### 3. 理解用户意图 + +用户可能以不同方式使用此 skill: + +**场景 A — 提供主题**:用户说"写一篇关于 XXX 的文章" +- 根据主题判断应该放在哪个章节(algorithm/architecture/project/qiniu-cloud) +- 如果章节不明确,询问用户 +- 按模板生成完整文档 + +**场景 B — 提供内容**:用户粘贴了一段文档或笔记 +- 分析内容主题,判断合适的章节 +- 整理成标准格式(参考 `references/article-template.md`) +- 如果内容不完整,询问用户补充 + +**场景 C — 更新现有文档**:用户说"更新 XXX 文章" +- 在仓库中找到对应文件 +- 根据用户要求更新内容 + +**场景 D — 章节不明确**:无法确定放在哪里 +- 列出当前仓库的章节结构供用户选择 +- 询问用户该内容属于哪个章节,或是否需要新建章节 + +### 4. 生成/更新文档 + +遵循 `references/article-template.md` 中的标准格式。关键要点: + +- 使用中文撰写 +- 代码示例以 Go 为主 +- 包含 mermaid 图表辅助说明 +- 包含常见陷阱和练习题 +- 文件名使用 kebab-case + +### 5. 更新导航 + +修改 `mkdocs.yml` 的 `nav` 字段,将新文档添加到正确位置,或根据用户指示修改已有条目。 + +参考 `references/mkdocs-nav.md` 了解当前导航结构和规范。 + +### 6. 提交并推送 + +```bash +cd "$TMP_DIR" +git add . +git commit -m "docs: 添加/更新 <文档标题>" +git push origin main +``` + +提交信息遵循 Conventional Commits 格式(参考全局 CLAUDE.md 规范)。 + +### 7. 清理 + +```bash +rm -rf "$TMP_DIR" +``` + +告知用户文档已推送,CI/CD 会自动部署。 + +## 交互原则 + +- **章节不确定时必须询问**:不要擅自决定文档位置 +- **内容不明确时必须询问**:不要编造技术内容 +- **确认用户意图**:在生成大量内容前,先确认主题和范围 +- **保持一致性**:新文档的风格应与现有文档一致 + +## 注意事项 + +- 根据用户指示可以修改已有的 nav 条目(重命名、移动位置、删除等) +- 如果需要新建目录,同时创建该目录的 `index.md` +- 推送后告知用户 CI/CD 部署通常需要 1-2 分钟 diff --git a/wiki/references/article-template.md b/wiki/references/article-template.md new file mode 100644 index 0000000..a75bc6e --- /dev/null +++ b/wiki/references/article-template.md @@ -0,0 +1,123 @@ +# 文章模板 + +本文档定义了文档站的标准文章格式。生成文档时必须遵循此模板。 + +## 文件命名规范 + +- 使用 kebab-case:`cache-breakdown.md`、`bloom-filter.md` +- 英文命名,内容用中文 +- 放入对应主题目录(algorithm/、architecture/、project/ 等) + +## 文章结构模板 + +```markdown +# 文章标题 + +!!! note "一句话摘要,概括本文核心内容" + +--- + +## 核心概念 + +1. **概念一** — 简要说明 +2. **概念二** — 简要说明 +3. **概念三** — 简要说明 + +## 详解 + +### 原理/机制 + +用文字和图表解释核心原理。推荐使用 mermaid 图表: + +```mermaid +graph TD + A[开始] --> B[处理] + B --> C[结束] +``` + +### 关键参数/配置 + +| 参数 | 说明 | 默认值 | +|------|------|--------| +| param1 | 描述 | value1 | +| param2 | 描述 | value2 | + +### 对比分析(如有) + +| 特性 | 方案 A | 方案 B | +|------|--------|--------| +| 特点1 | ... | ... | + +## 代码示例 + +```go +// 以 Go 语言为主,注释用中文 +func example() { + // 实现细节 +} +``` + +## 常见陷阱 + +!!! warning "陷阱一" + 描述常见错误及其规避方法。 + +!!! warning "陷阱二" + 描述另一个需要注意的问题。 + +## 练习题 + +??? question "题目一:xxx?" + ??? success "答案" + 解答内容。 + +??? question "题目二:xxx?" + ??? success "答案" + 解答内容。 + +## 相关链接 + +- [链接名称](URL) — 简要说明 +- [链接名称](URL) — 简要说明 +``` + +## 各部分写作要点 + +### H1 标题 +- 简洁明了,使用中文 +- 不超过 20 个字 + +### note admonition(摘要) +- 一句话概括全文核心 +- 让读者 3 秒内判断是否需要阅读 + +### 核心概念 +- 3-5 个关键概念 +- 每个概念一句话说明 +- 有序列表 + +### 详解 +- 分小节深入讲解 +- 优先使用 mermaid 图表辅助说明(流程图、序列图、状态图) +- 用表格做对比和参数说明 +- 代码块用于展示关键逻辑 +- 详解部分是文章主体,应占全文 60% 以上 + +### 代码示例 +- 完整可运行的 Go 代码 +- 中文注释,解释关键逻辑 +- 遵循 Go 命名规范(camelCase 变量,PascalCase 导出) + +### 常见陷阱 +- 使用 `!!! warning` admonition +- 每个陷阱说明:错误现象、原因、解决方案 +- 2-4 个 + +### 练习题 +- 使用 `??? question` 可折叠 +- 答案用嵌套的 `??? success "答案"` +- 2-3 道题,覆盖文章核心知识点 + +### 相关链接 +- 外部参考链接 +- 每个链接附简要说明 diff --git a/wiki/references/mkdocs-nav.md b/wiki/references/mkdocs-nav.md new file mode 100644 index 0000000..3ca5c40 --- /dev/null +++ b/wiki/references/mkdocs-nav.md @@ -0,0 +1,121 @@ +# MkDocs 导航结构参考 + +本文档记录文档站的 mkdocs.yml 配置和导航组织规范。 + +## 导航结构 + +文档站的导航由 `mkdocs.yml` 的 `nav` 字段控制。文档本身不使用 YAML frontmatter。 + +### 当前目录结构 + +``` +docs/ +├── index.md # 首页 +├── javascripts/mathjax.js # MathJax 配置 +├── algorithm/ # 算法 +│ ├── index.md # 章节首页 +│ ├── bloom-filter.md +│ ├── bloom-filter-deletion.md +│ ├── counting-bloom-filter.md +│ ├── cuckoo-filter.md +│ ├── heavykeeper.md +│ ├── binary-tree-traversal.md +│ └── queue.md +├── architecture/ # 架构 +│ ├── index.md +│ └── cache/ +│ ├── index.md +│ ├── cache-breakdown.md +│ ├── cache-avalanche.md +│ └── cache-penetration.md +├── project/ # 项目 +│ ├── index.md +│ ├── openapi-billing-architecture.md +│ ├── openapi-billing-resume.md +│ └── qdata/ +│ ├── index.md +│ ├── architecture.md +│ ├── data-governance-etl.md +│ ├── deployment.md +│ └── resume.md +└── qiniu-cloud/ # 七牛云 + ├── index.md + ├── architecture.md + ├── ansible-automation.md + ├── deployment-cicd.md + ├── security-auth-data.md + └── resume-tech-points.md +``` + +### nav 格式示例 + +```yaml +nav: + - 首页: index.md + - 项目: + - 项目概述: project/index.md + - OpenAPI 计费: + - 架构设计: project/openapi-billing-architecture.md + - 简历亮点: project/openapi-billing-resume.md + - qData: + - 概述: project/qdata/index.md + - 架构设计: project/qdata/architecture.md + - 数据治理: project/qdata/data-governance-etl.md + - 部署方案: project/qdata/deployment.md + - 简历亮点: project/qdata/resume.md + - 架构: + - 架构概述: architecture/index.md + - 缓存: + - 缓存概述: architecture/cache/index.md + - 缓存击穿: architecture/cache/cache-breakdown.md + - 缓存雪崩: architecture/cache/cache-avalanche.md + - 缓存穿透: architecture/cache/cache-penetration.md + - 算法: + - 算法概述: algorithm/index.md + - 布隆过滤器: algorithm/bloom-filter.md + - 布隆过滤器删除: algorithm/bloom-filter-deletion.md + - 计数布隆过滤器: algorithm/counting-bloom-filter.md + - 布谷鸟过滤器: algorithm/cuckoo-filter.md + - HeavyKeeper: algorithm/heavykeeper.md + - 二叉树遍历: algorithm/binary-tree-traversal.md + - 队列: algorithm/queue.md + - 七牛云: + - 概述: qiniu-cloud/index.md + - 架构设计: qiniu-cloud/architecture.md + - Ansible 自动化: qiniu-cloud/ansible-automation.md + - 部署与 CI/CD: qiniu-cloud/deployment-cicd.md + - 安全与认证: qiniu-cloud/security-auth-data.md + - 简历技术亮点: qiniu-cloud/resume-tech-points.md +``` + +## 添加新文档的步骤 + +1. **确定章节**:新文档属于哪个顶级章节(algorithm/architecture/project/qiniu-cloud)?是否有子章节? +2. **创建文件**:在对应目录下创建 `.md` 文件(kebab-case 命名) +3. **更新 nav**:在 `mkdocs.yml` 的 `nav` 中对应位置添加条目 +4. **更新 index.md**(如需):如果是新子章节,可能需要更新章节的 `index.md` + +## Section Index 规范 + +每个章节目录下的 `index.md` 是该章节的首页/概述页。使用 MkDocs Material 的 `navigation.indexes` 功能: + +```yaml +# mkdocs.yml 中启用 +theme: + features: + - navigation.indexes +``` + +`index.md` 通常包含: +- H1 标题(章节名称) +- 一句话概述 +- 子文章的链接表格(如有子文章) + +## 命名规范 + +| 类型 | 规范 | 示例 | +|------|------|------| +| 目录名 | 小写 kebab-case | `architecture/`、`qiniu-cloud/` | +| 文件名 | 小写 kebab-case | `cache-breakdown.md` | +| 章节标题 | 中文 | `# 缓存击穿` | +| nav 标签 | 中文 | `缓存击穿: architecture/cache/cache-breakdown.md` | diff --git a/wiki/scripts/check_ssh.sh b/wiki/scripts/check_ssh.sh new file mode 100755 index 0000000..f4333c9 --- /dev/null +++ b/wiki/scripts/check_ssh.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# 检查当前环境是否有可用的 SSH 私钥,并测试到 Gitea 仓库的连通性 +# 用法: bash check_ssh.sh [host] [port] +# 默认: 47.121.181.112:222 + +set -euo pipefail + +HOST="${1:-47.121.181.112}" +PORT="${2:-222}" +USER="git" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +echo "=== SSH 私钥检查 ===" + +# 1. 检查 ssh-agent 中是否有加载的密钥 +if ssh-add -l 2>/dev/null | grep -q "."; then + echo -e "${GREEN}[✓]${NC} ssh-agent 中有已加载的密钥" +else + echo -e "${YELLOW}[!]${NC} ssh-agent 中没有加载的密钥,将尝试使用 ~/.ssh/ 中的默认密钥" +fi + +# 2. 检查常见私钥文件是否存在 +FOUND_KEY=false +for key_file in ~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_ecdsa ~/.ssh/id_dsa; do + if [ -f "$key_file" ]; then + echo -e "${GREEN}[✓]${NC} 找到私钥: $key_file" + FOUND_KEY=true + break + fi +done + +if [ "$FOUND_KEY" = false ]; then + echo -e "${RED}[✗]${NC} 未找到任何默认 SSH 私钥(~/.ssh/id_ed25519, id_rsa, id_ecdsa, id_dsa)" + echo "" + echo "请生成一个新的 SSH 密钥:" + echo " ssh-keygen -t ed25519 -C 'your_email@example.com'" + echo "" + echo "然后将公钥添加到 Gitea:" + echo " cat ~/.ssh/id_ed25519.pub" + echo " # 在 Gitea 的 Settings → SSH Keys 中添加" + exit 1 +fi + +# 3. 测试 SSH 连通性 +echo "" +echo "=== 测试 SSH 连通性 ===" +echo "连接到 ${USER}@${HOST}:${PORT} ..." + +if ssh -o StrictHostKeyChecking=accept-new \ + -o ConnectTimeout=10 \ + -o BatchMode=yes \ + -p "$PORT" \ + "${USER}@${HOST}" 2>&1 | grep -qi "welcome\|gitea\|hello\|ssh"; then + echo -e "${GREEN}[✓]${NC} SSH 连接成功" + exit 0 +else + # ssh 返回非 0 但可能仍然成功(Gitea 会断开连接,返回码可能不为 0) + # 尝试实际 git ls-remote 来验证 + if git ls-remote "ssh://${USER}@${HOST}:${PORT}/wonder/docs.git" HEAD >/dev/null 2>&1; then + echo -e "${GREEN}[✓]${NC} SSH 认证成功(可以访问仓库)" + exit 0 + else + echo -e "${RED}[✗]${NC} SSH 连接失败" + echo "" + echo "可能的原因:" + echo " 1. 私钥未添加到 Gitea" + echo " 2. SSH 代理未运行" + echo " 3. 网络不通" + echo "" + echo "请尝试:" + echo " eval \$(ssh-agent -s)" + echo " ssh-add ~/.ssh/id_ed25519" + echo " ssh -p $PORT ${USER}@${HOST}" + exit 1 + fi +fi