Hermes-agent
This commit is contained in:
+132
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: "代码库检查 — 使用 pygount 检查代码库:代码行数、语言、占比"
|
||||
sidebar_label: "代码库检查"
|
||||
description: "使用 pygount 检查代码库:代码行数、语言、占比"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# 代码库检查
|
||||
|
||||
使用 pygount 检查代码库:代码行数、语言、占比。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/github/codebase-inspection` |
|
||||
| 版本 | `1.0.0` |
|
||||
| 作者 | Hermes Agent |
|
||||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `LOC`, `Code Analysis`, `pygount`, `Codebase`, `Metrics`, `Repository` |
|
||||
| 相关 skill | [`github-repo-management`](/user-guide/skills/bundled/github/github-github-repo-management) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 agent 在 skill 激活时所看到的指令内容。
|
||||
:::
|
||||
|
||||
# 使用 pygount 进行代码库检查
|
||||
|
||||
使用 `pygount` 分析仓库的代码行数、语言分布、文件数量及代码与注释的比例。
|
||||
|
||||
## 使用场景
|
||||
|
||||
- 用户请求统计 LOC(lines of code,代码行数)
|
||||
- 用户需要仓库的语言分布情况
|
||||
- 用户询问代码库的规模或组成
|
||||
- 用户需要代码与注释的比例
|
||||
- 一般性的"这个仓库有多大"问题
|
||||
|
||||
## 前置条件
|
||||
|
||||
```bash
|
||||
pip install --break-system-packages pygount 2>/dev/null || pip install pygount
|
||||
```
|
||||
|
||||
## 1. 基本摘要(最常用)
|
||||
|
||||
获取包含文件数量、代码行数和注释行数的完整语言分布:
|
||||
|
||||
```bash
|
||||
cd /path/to/repo
|
||||
pygount --format=summary \
|
||||
--folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,.eggs,*.egg-info" \
|
||||
.
|
||||
```
|
||||
|
||||
**重要:** 始终使用 `--folders-to-skip` 排除依赖/构建目录,否则 pygount 会遍历这些目录,导致运行时间极长甚至卡死。
|
||||
|
||||
## 2. 常用目录排除项
|
||||
|
||||
根据项目类型进行调整:
|
||||
|
||||
```bash
|
||||
# Python 项目
|
||||
--folders-to-skip=".git,venv,.venv,__pycache__,.cache,dist,build,.tox,.eggs,.mypy_cache"
|
||||
|
||||
# JavaScript/TypeScript 项目
|
||||
--folders-to-skip=".git,node_modules,dist,build,.next,.cache,.turbo,coverage"
|
||||
|
||||
# 通用兜底
|
||||
--folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,vendor,third_party"
|
||||
```
|
||||
|
||||
## 3. 按特定语言过滤
|
||||
|
||||
```bash
|
||||
# 仅统计 Python 文件
|
||||
pygount --suffix=py --format=summary .
|
||||
|
||||
# 仅统计 Python 和 YAML
|
||||
pygount --suffix=py,yaml,yml --format=summary .
|
||||
```
|
||||
|
||||
## 4. 逐文件详细输出
|
||||
|
||||
```bash
|
||||
# 默认格式显示每个文件的详细信息
|
||||
pygount --folders-to-skip=".git,node_modules,venv" .
|
||||
|
||||
# 按代码行数排序(通过管道传给 sort)
|
||||
pygount --folders-to-skip=".git,node_modules,venv" . | sort -t$'\t' -k1 -nr | head -20
|
||||
```
|
||||
|
||||
## 5. 输出格式
|
||||
|
||||
```bash
|
||||
# 摘要表格(默认推荐)
|
||||
pygount --format=summary .
|
||||
|
||||
# JSON 输出,适合程序化处理
|
||||
pygount --format=json .
|
||||
|
||||
# 管道友好:语言、文件数、代码行、文档行、空行、字符串行
|
||||
pygount --format=summary . 2>/dev/null
|
||||
```
|
||||
|
||||
## 6. 结果解读
|
||||
|
||||
摘要表格各列说明:
|
||||
- **Language** — 检测到的编程语言
|
||||
- **Files** — 该语言的文件数量
|
||||
- **Code** — 实际代码行数(可执行/声明性语句)
|
||||
- **Comment** — 注释或文档行数
|
||||
- **%** — 占总量的百分比
|
||||
|
||||
特殊伪语言:
|
||||
- `__empty__` — 空文件
|
||||
- `__binary__` — 二进制文件(图片、编译产物等)
|
||||
- `__generated__` — 自动生成的文件(启发式检测)
|
||||
- `__duplicate__` — 内容完全相同的文件
|
||||
- `__unknown__` — 无法识别的文件类型
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **始终排除 .git、node_modules、venv** — 不使用 `--folders-to-skip` 时,pygount 会遍历所有内容,在大型依赖树上可能耗时数分钟甚至卡死。
|
||||
2. **Markdown 显示 0 代码行** — pygount 将所有 Markdown 内容归类为注释而非代码,这是预期行为。
|
||||
3. **JSON 文件代码行数偏低** — pygount 统计 JSON 行数时可能较为保守,如需精确统计 JSON 行数,请直接使用 `wc -l`。
|
||||
4. **大型 monorepo** — 对于非常大的仓库,建议使用 `--suffix` 指定目标语言,而非扫描全部内容。
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
---
|
||||
title: "Github Auth — GitHub auth setup: HTTPS tokens, SSH keys, gh CLI login"
|
||||
sidebar_label: "Github Auth"
|
||||
description: "GitHub auth 设置:HTTPS 令牌、SSH 密钥、gh CLI 登录"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Auth
|
||||
|
||||
GitHub auth 设置:HTTPS 令牌、SSH 密钥、gh CLI 登录。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/github/github-auth` |
|
||||
| 版本 | `1.1.0` |
|
||||
| 作者 | Hermes Agent |
|
||||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `GitHub`, `Authentication`, `Git`, `gh-cli`, `SSH`, `Setup` |
|
||||
| 相关 skill | [`github-pr-workflow`](/user-guide/skills/bundled/github/github-github-pr-workflow), [`github-code-review`](/user-guide/skills/bundled/github/github-github-code-review), [`github-issues`](/user-guide/skills/bundled/github/github-github-issues), [`github-repo-management`](/user-guide/skills/bundled/github/github-github-repo-management) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 agent 在 skill 激活时所看到的指令内容。
|
||||
:::
|
||||
|
||||
# GitHub 认证设置
|
||||
|
||||
此 skill 用于配置认证,使 agent 能够操作 GitHub 仓库、PR、issue 和 CI。涵盖两条路径:
|
||||
|
||||
- **`git`(始终可用)** — 使用 HTTPS 个人访问令牌(personal access token)或 SSH 密钥
|
||||
- **`gh` CLI(如已安装)** — 更丰富的 GitHub API 访问,认证流程更简单
|
||||
|
||||
## 检测流程
|
||||
|
||||
当用户要求你操作 GitHub 时,首先执行以下检查:
|
||||
|
||||
```bash
|
||||
# Check what's available
|
||||
git --version
|
||||
gh --version 2>/dev/null || echo "gh not installed"
|
||||
|
||||
# Check if already authenticated
|
||||
gh auth status 2>/dev/null || echo "gh not authenticated"
|
||||
git config --global credential.helper 2>/dev/null || echo "no git credential helper"
|
||||
```
|
||||
|
||||
**决策树:**
|
||||
1. 若 `gh auth status` 显示已认证 → 直接使用 `gh` 处理所有操作
|
||||
2. 若 `gh` 已安装但未认证 → 使用下方"gh auth"方法
|
||||
3. 若 `gh` 未安装 → 使用下方"仅 git"方法(无需 sudo)
|
||||
|
||||
---
|
||||
|
||||
## 方法一:仅 Git 认证(无 gh,无 sudo)
|
||||
|
||||
适用于任何已安装 `git` 的机器,无需 root 权限。
|
||||
|
||||
### 选项 A:HTTPS 配合个人访问令牌(推荐)
|
||||
|
||||
最通用的方法——适用于所有环境,无需 SSH 配置。
|
||||
|
||||
**第一步:创建个人访问令牌**
|
||||
|
||||
告知用户访问:**https://github.com/settings/tokens**
|
||||
|
||||
- 点击"Generate new token (classic)"
|
||||
- 填写名称,如"hermes-agent"
|
||||
- 选择权限范围(scope):
|
||||
- `repo`(完整仓库访问——读、写、推送、PR)
|
||||
- `workflow`(触发和管理 GitHub Actions)
|
||||
- `read:org`(如需操作组织仓库)
|
||||
- 设置有效期(90 天是合理的默认值)
|
||||
- 复制令牌——此后不会再次显示
|
||||
|
||||
**第二步:配置 git 存储令牌**
|
||||
|
||||
```bash
|
||||
# Set up the credential helper to cache credentials
|
||||
# "store" saves to ~/.git-credentials in plaintext (simple, persistent)
|
||||
git config --global credential.helper store
|
||||
|
||||
# Now do a test operation that triggers auth — git will prompt for credentials
|
||||
# Username: <their-github-username>
|
||||
# Password: <paste the personal access token, NOT their GitHub password>
|
||||
git ls-remote https://github.com/<their-username>/<any-repo>.git
|
||||
```
|
||||
|
||||
首次输入凭据后,将被保存并在后续所有操作中复用。
|
||||
|
||||
**替代方案:cache helper(凭据在内存中过期)**
|
||||
|
||||
```bash
|
||||
# Cache in memory for 8 hours (28800 seconds) instead of saving to disk
|
||||
git config --global credential.helper 'cache --timeout=28800'
|
||||
```
|
||||
|
||||
**替代方案:直接将令牌写入远程 URL(按仓库设置)**
|
||||
|
||||
```bash
|
||||
# Embed token in the remote URL (avoids credential prompts entirely)
|
||||
git remote set-url origin https://<username>:<token>@github.com/<owner>/<repo>.git
|
||||
```
|
||||
|
||||
**第三步:配置 git 身份信息**
|
||||
|
||||
```bash
|
||||
# Required for commits — set name and email
|
||||
git config --global user.name "Their Name"
|
||||
git config --global user.email "their-email@example.com"
|
||||
```
|
||||
|
||||
**第四步:验证**
|
||||
|
||||
```bash
|
||||
# Test push access (this should work without any prompts now)
|
||||
git ls-remote https://github.com/<their-username>/<any-repo>.git
|
||||
|
||||
# Verify identity
|
||||
git config --global user.name
|
||||
git config --global user.email
|
||||
```
|
||||
|
||||
### 选项 B:SSH 密钥认证
|
||||
|
||||
适合偏好 SSH 或已有密钥的用户。
|
||||
|
||||
**第一步:检查现有 SSH 密钥**
|
||||
|
||||
```bash
|
||||
ls -la ~/.ssh/id_*.pub 2>/dev/null || echo "No SSH keys found"
|
||||
```
|
||||
|
||||
**第二步:如需则生成密钥**
|
||||
|
||||
```bash
|
||||
# Generate an ed25519 key (modern, secure, fast)
|
||||
ssh-keygen -t ed25519 -C "their-email@example.com" -f ~/.ssh/id_ed25519 -N ""
|
||||
|
||||
# Display the public key for them to add to GitHub
|
||||
cat ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
告知用户在以下地址添加公钥:**https://github.com/settings/keys**
|
||||
- 点击"New SSH key"
|
||||
- 粘贴公钥内容
|
||||
- 填写标题,如"hermes-agent-<machine-name>"
|
||||
|
||||
**第三步:测试连接**
|
||||
|
||||
```bash
|
||||
ssh -T git@github.com
|
||||
# Expected: "Hi <username>! You've successfully authenticated..."
|
||||
```
|
||||
|
||||
**第四步:配置 git 使用 SSH 访问 GitHub**
|
||||
|
||||
```bash
|
||||
# Rewrite HTTPS GitHub URLs to SSH automatically
|
||||
git config --global url."git@github.com:".insteadOf "https://github.com/"
|
||||
```
|
||||
|
||||
**第五步:配置 git 身份信息**
|
||||
|
||||
```bash
|
||||
git config --global user.name "Their Name"
|
||||
git config --global user.email "their-email@example.com"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方法二:gh CLI 认证
|
||||
|
||||
若已安装 `gh`,一步即可完成 API 访问和 git 凭据配置。
|
||||
|
||||
### 浏览器交互登录(桌面环境)
|
||||
|
||||
```bash
|
||||
gh auth login
|
||||
# Select: GitHub.com
|
||||
# Select: HTTPS
|
||||
# Authenticate via browser
|
||||
```
|
||||
|
||||
### 基于令牌登录(无头环境 / SSH 服务器)
|
||||
|
||||
```bash
|
||||
echo "<THEIR_TOKEN>" | gh auth login --with-token
|
||||
|
||||
# Set up git credentials through gh
|
||||
gh auth setup-git
|
||||
```
|
||||
|
||||
### 验证
|
||||
|
||||
```bash
|
||||
gh auth status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 不使用 gh 调用 GitHub API
|
||||
|
||||
当 `gh` 不可用时,仍可使用 `curl` 配合个人访问令牌访问完整的 GitHub API。其他 GitHub skill 的降级方案均采用此方式。
|
||||
|
||||
### 为 API 调用设置令牌
|
||||
|
||||
```bash
|
||||
# Option 1: Export as env var (preferred — keeps it out of commands)
|
||||
export GITHUB_TOKEN="<token>"
|
||||
|
||||
# Then use in curl calls:
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/user
|
||||
```
|
||||
|
||||
### 从 Git 凭据中提取令牌
|
||||
|
||||
若已通过 `credential.helper store` 配置 git 凭据,可提取令牌:
|
||||
|
||||
```bash
|
||||
# Read from git credential store
|
||||
grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|'
|
||||
```
|
||||
|
||||
### 辅助函数:检测认证方式
|
||||
|
||||
在任何 GitHub 工作流开始时使用此模式:
|
||||
|
||||
```bash
|
||||
# Try gh first, fall back to git + curl
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
echo "AUTH_METHOD=gh"
|
||||
elif [ -n "$GITHUB_TOKEN" ]; then
|
||||
echo "AUTH_METHOD=curl"
|
||||
elif [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then
|
||||
export GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
echo "AUTH_METHOD=curl"
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
export GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
echo "AUTH_METHOD=curl"
|
||||
else
|
||||
echo "AUTH_METHOD=none"
|
||||
echo "Need to set up authentication first"
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|---------|----------|
|
||||
| `git push` 要求输入密码 | GitHub 已禁用密码认证。请使用个人访问令牌作为密码,或切换至 SSH |
|
||||
| `remote: Permission to X denied` | 令牌可能缺少 `repo` scope——请重新生成并选择正确的 scope |
|
||||
| `fatal: Authentication failed` | 缓存的凭据可能已过期——运行 `git credential reject` 后重新认证 |
|
||||
| `ssh: connect to host github.com port 22: Connection refused` | 尝试通过 HTTPS 端口使用 SSH:在 `~/.ssh/config` 中为 `Host github.com` 添加 `Port 443` 和 `Hostname ssh.github.com` |
|
||||
| 凭据不持久 | 检查 `git config --global credential.helper`——必须为 `store` 或 `cache` |
|
||||
| 多个 GitHub 账号 | 在 `~/.ssh/config` 中为不同主机别名配置不同 SSH 密钥,或使用按仓库设置的凭据 URL |
|
||||
| `gh: command not found` 且无 sudo | 使用上方方法一(仅 git)——无需安装任何软件 |
|
||||
+499
@@ -0,0 +1,499 @@
|
||||
---
|
||||
title: "Github Code Review — 通过 gh 或 REST 审查 PR:差异对比、行内评论"
|
||||
sidebar_label: "Github Code Review"
|
||||
description: "通过 gh 或 REST 审查 PR:差异对比、行内评论"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Code Review
|
||||
|
||||
通过 gh 或 REST 审查 PR:差异对比、行内评论。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/github/github-code-review` |
|
||||
| 版本 | `1.1.0` |
|
||||
| 作者 | Hermes Agent |
|
||||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `GitHub`, `Code-Review`, `Pull-Requests`, `Git`, `Quality` |
|
||||
| 相关 skill | [`github-auth`](/user-guide/skills/bundled/github/github-github-auth), [`github-pr-workflow`](/user-guide/skills/bundled/github/github-github-pr-workflow) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 agent 在 skill 激活时所看到的指令内容。
|
||||
:::
|
||||
|
||||
# GitHub Code Review
|
||||
|
||||
在推送前对本地变更执行代码审查,或审查 GitHub 上的开放 PR。此 skill 大部分功能使用纯 `git` 命令——`gh`/`curl` 的区别仅在 PR 级别的交互中才有意义。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 已通过 GitHub 身份验证(参见 `github-auth` skill)
|
||||
- 位于 git 仓库内部
|
||||
|
||||
### 设置(用于 PR 交互)
|
||||
|
||||
```bash
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
AUTH="gh"
|
||||
else
|
||||
AUTH="git"
|
||||
if [ -z "$GITHUB_TOKEN" ]; then
|
||||
if [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then
|
||||
GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
||||
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
|
||||
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 审查本地变更(推送前)
|
||||
|
||||
此部分为纯 `git` 操作——适用于所有环境,无需 API。
|
||||
|
||||
### 获取差异
|
||||
|
||||
```bash
|
||||
# 已暂存的变更(即将提交的内容)
|
||||
git diff --staged
|
||||
|
||||
# 相对于 main 的所有变更(PR 将包含的内容)
|
||||
git diff main...HEAD
|
||||
|
||||
# 仅显示文件名
|
||||
git diff main...HEAD --name-only
|
||||
|
||||
# 统计摘要(每个文件的插入/删除行数)
|
||||
git diff main...HEAD --stat
|
||||
```
|
||||
|
||||
### 审查策略
|
||||
|
||||
1. **先了解全局:**
|
||||
|
||||
```bash
|
||||
git diff main...HEAD --stat
|
||||
git log main..HEAD --oneline
|
||||
```
|
||||
|
||||
2. **逐文件审查**——使用 `read_file` 查看已变更文件的完整上下文,并通过差异了解具体改动:
|
||||
|
||||
```bash
|
||||
git diff main...HEAD -- src/auth/login.py
|
||||
```
|
||||
|
||||
3. **检查常见问题:**
|
||||
|
||||
```bash
|
||||
# 遗留的调试语句、TODO、console.log 等
|
||||
git diff main...HEAD | grep -n "print(\|console\.log\|TODO\|FIXME\|HACK\|XXX\|debugger"
|
||||
|
||||
# 意外暂存的大文件
|
||||
git diff main...HEAD --stat | sort -t'|' -k2 -rn | head -10
|
||||
|
||||
# 密钥或凭据模式
|
||||
git diff main...HEAD | grep -in "password\|secret\|api_key\|token.*=\|private_key"
|
||||
|
||||
# 合并冲突标记
|
||||
git diff main...HEAD | grep -n "<<<<<<\|>>>>>>\|======="
|
||||
```
|
||||
|
||||
4. **向用户呈现结构化反馈。**
|
||||
|
||||
### 审查输出格式
|
||||
|
||||
审查本地变更时,按以下结构呈现结果:
|
||||
|
||||
```
|
||||
## Code Review Summary
|
||||
|
||||
### Critical
|
||||
- **src/auth.py:45** — SQL injection: user input passed directly to query.
|
||||
Suggestion: Use parameterized queries.
|
||||
|
||||
### Warnings
|
||||
- **src/models/user.py:23** — Password stored in plaintext. Use bcrypt or argon2.
|
||||
- **src/api/routes.py:112** — No rate limiting on login endpoint.
|
||||
|
||||
### Suggestions
|
||||
- **src/utils/helpers.py:8** — Duplicates logic in `src/core/utils.py:34`. Consolidate.
|
||||
- **tests/test_auth.py** — Missing edge case: expired token test.
|
||||
|
||||
### Looks Good
|
||||
- Clean separation of concerns in the middleware layer
|
||||
- Good test coverage for the happy path
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 审查 GitHub 上的 Pull Request
|
||||
|
||||
### 查看 PR 详情
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh pr view 123
|
||||
gh pr diff 123
|
||||
gh pr diff 123 --name-only
|
||||
```
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
PR_NUMBER=123
|
||||
|
||||
# 获取 PR 详情
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
pr = json.load(sys.stdin)
|
||||
print(f\"Title: {pr['title']}\")
|
||||
print(f\"Author: {pr['user']['login']}\")
|
||||
print(f\"Branch: {pr['head']['ref']} -> {pr['base']['ref']}\")
|
||||
print(f\"State: {pr['state']}\")
|
||||
print(f\"Body:\n{pr['body']}\")"
|
||||
|
||||
# 列出已变更文件
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/files \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for f in json.load(sys.stdin):
|
||||
print(f\"{f['status']:10} +{f['additions']:-4} -{f['deletions']:-4} {f['filename']}\")"
|
||||
```
|
||||
|
||||
### 在本地检出 PR 进行完整审查
|
||||
|
||||
此操作使用纯 `git`——无需 `gh`:
|
||||
|
||||
```bash
|
||||
# 获取 PR 分支并检出
|
||||
git fetch origin pull/123/head:pr-123
|
||||
git checkout pr-123
|
||||
|
||||
# 现在可以使用 read_file、search_files、运行测试等
|
||||
|
||||
# 查看与基础分支的差异
|
||||
git diff main...pr-123
|
||||
```
|
||||
|
||||
**使用 gh(快捷方式):**
|
||||
|
||||
```bash
|
||||
gh pr checkout 123
|
||||
```
|
||||
|
||||
### 在 PR 上留下评论
|
||||
|
||||
**通用 PR 评论——使用 gh:**
|
||||
|
||||
```bash
|
||||
gh pr comment 123 --body "Overall looks good, a few suggestions below."
|
||||
```
|
||||
|
||||
**通用 PR 评论——使用 curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/$PR_NUMBER/comments \
|
||||
-d '{"body": "Overall looks good, a few suggestions below."}'
|
||||
```
|
||||
|
||||
### 留下行内审查评论
|
||||
|
||||
**单条行内评论——使用 gh(通过 API):**
|
||||
|
||||
```bash
|
||||
HEAD_SHA=$(gh pr view 123 --json headRefOid --jq '.headRefOid')
|
||||
|
||||
gh api repos/$OWNER/$REPO/pulls/123/comments \
|
||||
--method POST \
|
||||
-f body="This could be simplified with a list comprehension." \
|
||||
-f path="src/auth/login.py" \
|
||||
-f commit_id="$HEAD_SHA" \
|
||||
-f line=45 \
|
||||
-f side="RIGHT"
|
||||
```
|
||||
|
||||
**单条行内评论——使用 curl:**
|
||||
|
||||
```bash
|
||||
# 获取 head commit SHA
|
||||
HEAD_SHA=$(curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
|
||||
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments \
|
||||
-d "{
|
||||
\"body\": \"This could be simplified with a list comprehension.\",
|
||||
\"path\": \"src/auth/login.py\",
|
||||
\"commit_id\": \"$HEAD_SHA\",
|
||||
\"line\": 45,
|
||||
\"side\": \"RIGHT\"
|
||||
}"
|
||||
```
|
||||
|
||||
### 提交正式审查(批准 / 请求变更)
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh pr review 123 --approve --body "LGTM!"
|
||||
gh pr review 123 --request-changes --body "See inline comments."
|
||||
gh pr review 123 --comment --body "Some suggestions, nothing blocking."
|
||||
```
|
||||
|
||||
**使用 curl——原子性提交包含多条评论的审查:**
|
||||
|
||||
```bash
|
||||
HEAD_SHA=$(curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
|
||||
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews \
|
||||
-d "{
|
||||
\"commit_id\": \"$HEAD_SHA\",
|
||||
\"event\": \"COMMENT\",
|
||||
\"body\": \"Code review from Hermes Agent\",
|
||||
\"comments\": [
|
||||
{\"path\": \"src/auth.py\", \"line\": 45, \"body\": \"Use parameterized queries to prevent SQL injection.\"},
|
||||
{\"path\": \"src/models/user.py\", \"line\": 23, \"body\": \"Hash passwords with bcrypt before storing.\"},
|
||||
{\"path\": \"tests/test_auth.py\", \"line\": 1, \"body\": \"Add test for expired token edge case.\"}
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
事件值:`"APPROVE"`、`"REQUEST_CHANGES"`、`"COMMENT"`
|
||||
|
||||
`line` 字段指文件*新版本*中的行号。对于已删除的行,使用 `"side": "LEFT"`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 审查清单
|
||||
|
||||
执行代码审查(本地或 PR)时,系统性地检查以下内容:
|
||||
|
||||
### 正确性
|
||||
- 代码是否实现了其声称的功能?
|
||||
- 边界情况是否已处理(空输入、null、大数据、并发访问)?
|
||||
- 错误路径是否优雅处理?
|
||||
|
||||
### 安全性
|
||||
- 无硬编码的密钥、凭据或 API key
|
||||
- 对用户输入进行验证
|
||||
- 无 SQL 注入、XSS 或路径遍历
|
||||
- 在需要的地方进行身份验证/授权检查
|
||||
|
||||
### 代码质量
|
||||
- 命名清晰(变量、函数、类)
|
||||
- 无不必要的复杂性或过早抽象
|
||||
- DRY——无应提取的重复逻辑
|
||||
- 函数职责单一
|
||||
|
||||
### 测试
|
||||
- 新代码路径是否已测试?
|
||||
- 正常路径和错误情况是否已覆盖?
|
||||
- 测试是否可读且可维护?
|
||||
|
||||
### 性能
|
||||
- 无 N+1 查询或不必要的循环
|
||||
- 在适当位置使用缓存
|
||||
- 异步代码路径中无阻塞操作
|
||||
|
||||
### 文档
|
||||
- 公共 API 已文档化
|
||||
- 非显而易见的逻辑有注释说明"为什么"
|
||||
- 若行为发生变化,README 已更新
|
||||
|
||||
---
|
||||
|
||||
## 4. 推送前审查工作流
|
||||
|
||||
当用户要求"审查代码"或"推送前检查"时:
|
||||
|
||||
1. `git diff main...HEAD --stat`——了解变更范围
|
||||
2. `git diff main...HEAD`——阅读完整差异
|
||||
3. 对每个已变更的文件,如需更多上下文则使用 `read_file`
|
||||
4. 应用上述审查清单
|
||||
5. 按结构化格式呈现结果(Critical / Warnings / Suggestions / Looks Good)
|
||||
6. 若发现严重问题,在用户推送前主动提出修复
|
||||
|
||||
---
|
||||
|
||||
## 5. PR 审查工作流(端到端)
|
||||
|
||||
当用户要求"审查 PR #N"、"查看这个 PR",或提供 PR URL 时,按以下步骤执行:
|
||||
|
||||
### 第一步:设置环境
|
||||
|
||||
```bash
|
||||
source "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/gh-env.sh"
|
||||
# 或运行本 skill 顶部的内联设置代码块
|
||||
```
|
||||
|
||||
### 第二步:收集 PR 上下文
|
||||
|
||||
获取 PR 元数据、描述和已变更文件列表,在深入代码之前了解变更范围。
|
||||
|
||||
**使用 gh:**
|
||||
```bash
|
||||
gh pr view 123
|
||||
gh pr diff 123 --name-only
|
||||
gh pr checks 123
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
```bash
|
||||
PR_NUMBER=123
|
||||
|
||||
# PR 详情(标题、作者、描述、分支)
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER
|
||||
|
||||
# 带行数统计的已变更文件
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER/files
|
||||
```
|
||||
|
||||
### 第三步:在本地检出 PR
|
||||
|
||||
这样可以完整使用 `read_file`、`search_files`,以及运行测试的能力。
|
||||
|
||||
```bash
|
||||
git fetch origin pull/$PR_NUMBER/head:pr-$PR_NUMBER
|
||||
git checkout pr-$PR_NUMBER
|
||||
```
|
||||
|
||||
### 第四步:阅读差异并理解变更
|
||||
|
||||
```bash
|
||||
# 与基础分支的完整差异
|
||||
git diff main...HEAD
|
||||
|
||||
# 对于大型 PR,逐文件查看
|
||||
git diff main...HEAD --name-only
|
||||
# 然后对每个文件:
|
||||
git diff main...HEAD -- path/to/file.py
|
||||
```
|
||||
|
||||
对每个已变更的文件,使用 `read_file` 查看变更周围的完整上下文——仅凭差异可能遗漏只有在周围代码中才能发现的问题。
|
||||
|
||||
### 第五步:在本地运行自动化检查(如适用)
|
||||
|
||||
```bash
|
||||
# 若有测试套件,运行测试
|
||||
python -m pytest 2>&1 | tail -20
|
||||
# 或:npm test, cargo test, go test ./..., 等
|
||||
|
||||
# 若已配置,运行 linter
|
||||
ruff check . 2>&1 | head -30
|
||||
# 或:eslint, clippy, 等
|
||||
```
|
||||
|
||||
### 第六步:应用审查清单(第 3 节)
|
||||
|
||||
逐一检查每个类别:正确性、安全性、代码质量、测试、性能、文档。
|
||||
|
||||
### 第七步:将审查结果发布到 GitHub
|
||||
|
||||
汇总结果并以正式审查形式提交,附带行内评论。
|
||||
|
||||
**使用 gh:**
|
||||
```bash
|
||||
# 若无问题——批准
|
||||
gh pr review $PR_NUMBER --approve --body "Reviewed by Hermes Agent. Code looks clean — good test coverage, no security concerns."
|
||||
|
||||
# 若发现问题——请求变更并附行内评论
|
||||
gh pr review $PR_NUMBER --request-changes --body "Found a few issues — see inline comments."
|
||||
```
|
||||
|
||||
**使用 curl——原子性提交包含多条行内评论的审查:**
|
||||
```bash
|
||||
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
|
||||
|
||||
# 构建审查 JSON——event 为 APPROVE、REQUEST_CHANGES 或 COMMENT
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER/reviews \
|
||||
-d "{
|
||||
\"commit_id\": \"$HEAD_SHA\",
|
||||
\"event\": \"REQUEST_CHANGES\",
|
||||
\"body\": \"## Hermes Agent Review\n\nFound 2 issues, 1 suggestion. See inline comments.\",
|
||||
\"comments\": [
|
||||
{\"path\": \"src/auth.py\", \"line\": 45, \"body\": \"🔴 **Critical:** User input passed directly to SQL query — use parameterized queries.\"},
|
||||
{\"path\": \"src/models.py\", \"line\": 23, \"body\": \"⚠️ **Warning:** Password stored without hashing.\"},
|
||||
{\"path\": \"src/utils.py\", \"line\": 8, \"body\": \"💡 **Suggestion:** This duplicates logic in core/utils.py:34.\"}
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
### 第八步:同时发布摘要评论
|
||||
|
||||
除行内评论外,还需留下顶层摘要,让 PR 作者一目了然地了解全貌。使用 `references/review-output-template.md` 中的审查输出格式。
|
||||
|
||||
**使用 gh:**
|
||||
```bash
|
||||
gh pr comment $PR_NUMBER --body "$(cat <<'EOF'
|
||||
## Code Review Summary
|
||||
|
||||
**Verdict: Changes Requested** (2 issues, 1 suggestion)
|
||||
|
||||
### 🔴 Critical
|
||||
- **src/auth.py:45** — SQL injection vulnerability
|
||||
|
||||
### ⚠️ Warnings
|
||||
- **src/models.py:23** — Plaintext password storage
|
||||
|
||||
### 💡 Suggestions
|
||||
- **src/utils.py:8** — Duplicated logic, consider consolidating
|
||||
|
||||
### ✅ Looks Good
|
||||
- Clean API design
|
||||
- Good error handling in the middleware layer
|
||||
|
||||
---
|
||||
*Reviewed by Hermes Agent*
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### 第九步:清理
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git branch -D pr-$PR_NUMBER
|
||||
```
|
||||
|
||||
### 决策:批准 vs 请求变更 vs 评论
|
||||
|
||||
- **批准(Approve)**——无严重或警告级别的问题,仅有次要建议或完全通过
|
||||
- **请求变更(Request Changes)**——存在任何在合并前应修复的严重或警告级别问题
|
||||
- **评论(Comment)**——有观察和建议,但无阻塞性问题(在不确定或 PR 为草稿时使用)
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
---
|
||||
title: "Github Issues — 通过 gh 或 REST 创建、分类、标记、分配 GitHub Issues"
|
||||
sidebar_label: "Github Issues"
|
||||
description: "通过 gh 或 REST 创建、分类、标记、分配 GitHub Issues"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Issues
|
||||
|
||||
通过 gh 或 REST 创建、分类、标记、分配 GitHub Issues。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/github/github-issues` |
|
||||
| 版本 | `1.1.0` |
|
||||
| 作者 | Hermes Agent |
|
||||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `GitHub`, `Issues`, `Project-Management`, `Bug-Tracking`, `Triage` |
|
||||
| 相关 skills | [`github-auth`](/user-guide/skills/bundled/github/github-github-auth), [`github-pr-workflow`](/user-guide/skills/bundled/github/github-github-pr-workflow) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 skill 激活时 agent 所看到的指令内容。
|
||||
:::
|
||||
|
||||
# GitHub Issues 管理
|
||||
|
||||
创建、搜索、分类和管理 GitHub Issues。每个章节先展示 `gh` 命令,再展示 `curl` 备用方案。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 已通过 GitHub 认证(参见 `github-auth` skill)
|
||||
- 位于含有 GitHub 远程仓库的 git 仓库内,或显式指定仓库
|
||||
|
||||
### 设置
|
||||
|
||||
```bash
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
AUTH="gh"
|
||||
else
|
||||
AUTH="git"
|
||||
if [ -z "$GITHUB_TOKEN" ]; then
|
||||
if [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then
|
||||
GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
||||
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
|
||||
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 查看 Issues
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue list
|
||||
gh issue list --state open --label "bug"
|
||||
gh issue list --assignee @me
|
||||
gh issue list --search "authentication error" --state all
|
||||
gh issue view 42
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# 列出开放的 issues
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?state=open&per_page=20" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin):
|
||||
if 'pull_request' not in i: # GitHub API returns PRs in /issues too
|
||||
labels = ', '.join(l['name'] for l in i['labels'])
|
||||
print(f\"#{i['number']:5} {i['state']:6} {labels:30} {i['title']}\")"
|
||||
|
||||
# 按标签过滤
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?state=open&labels=bug&per_page=20" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin):
|
||||
if 'pull_request' not in i:
|
||||
print(f\"#{i['number']} {i['title']}\")"
|
||||
|
||||
# 查看特定 issue
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42 \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
i = json.load(sys.stdin)
|
||||
labels = ', '.join(l['name'] for l in i['labels'])
|
||||
assignees = ', '.join(a['login'] for a in i['assignees'])
|
||||
print(f\"#{i['number']}: {i['title']}\")
|
||||
print(f\"State: {i['state']} Labels: {labels} Assignees: {assignees}\")
|
||||
print(f\"Author: {i['user']['login']} Created: {i['created_at']}\")
|
||||
print(f\"\n{i['body']}\")"
|
||||
|
||||
# 搜索 issues
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/search/issues?q=authentication+error+repo:$OWNER/$REPO" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin)['items']:
|
||||
print(f\"#{i['number']} {i['state']:6} {i['title']}\")"
|
||||
```
|
||||
|
||||
## 2. 创建 Issues
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue create \
|
||||
--title "Login redirect ignores ?next= parameter" \
|
||||
--body "## Description
|
||||
After logging in, users always land on /dashboard.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Navigate to /settings while logged out
|
||||
2. Get redirected to /login?next=/settings
|
||||
3. Log in
|
||||
4. Actual: redirected to /dashboard (should go to /settings)
|
||||
|
||||
## Expected Behavior
|
||||
Respect the ?next= query parameter." \
|
||||
--label "bug,backend" \
|
||||
--assignee "username"
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues \
|
||||
-d '{
|
||||
"title": "Login redirect ignores ?next= parameter",
|
||||
"body": "## Description\nAfter logging in, users always land on /dashboard.\n\n## Steps to Reproduce\n1. Navigate to /settings while logged out\n2. Get redirected to /login?next=/settings\n3. Log in\n4. Actual: redirected to /dashboard\n\n## Expected Behavior\nRespect the ?next= query parameter.",
|
||||
"labels": ["bug", "backend"],
|
||||
"assignees": ["username"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Bug 报告模板
|
||||
|
||||
```
|
||||
## Bug Description
|
||||
<What's happening>
|
||||
|
||||
## Steps to Reproduce
|
||||
1. <step>
|
||||
2. <step>
|
||||
|
||||
## Expected Behavior
|
||||
<What should happen>
|
||||
|
||||
## Actual Behavior
|
||||
<What actually happens>
|
||||
|
||||
## Environment
|
||||
- OS: <os>
|
||||
- Version: <version>
|
||||
```
|
||||
|
||||
### 功能请求模板
|
||||
|
||||
```
|
||||
## Feature Description
|
||||
<What you want>
|
||||
|
||||
## Motivation
|
||||
<Why this would be useful>
|
||||
|
||||
## Proposed Solution
|
||||
<How it could work>
|
||||
|
||||
## Alternatives Considered
|
||||
<Other approaches>
|
||||
```
|
||||
|
||||
## 3. 管理 Issues
|
||||
|
||||
### 添加/移除标签
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue edit 42 --add-label "priority:high,bug"
|
||||
gh issue edit 42 --remove-label "needs-triage"
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# 添加标签
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/labels \
|
||||
-d '{"labels": ["priority:high", "bug"]}'
|
||||
|
||||
# 移除标签
|
||||
curl -s -X DELETE \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/labels/needs-triage
|
||||
|
||||
# 列出仓库中可用的标签
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/labels \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for l in json.load(sys.stdin):
|
||||
print(f\" {l['name']:30} {l.get('description', '')}\")"
|
||||
```
|
||||
|
||||
### 分配
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue edit 42 --add-assignee username
|
||||
gh issue edit 42 --add-assignee @me
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/assignees \
|
||||
-d '{"assignees": ["username"]}'
|
||||
```
|
||||
|
||||
### 评论
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue comment 42 --body "Investigated — root cause is in auth middleware. Working on a fix."
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/comments \
|
||||
-d '{"body": "Investigated — root cause is in auth middleware. Working on a fix."}'
|
||||
```
|
||||
|
||||
### 关闭与重新开启
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue close 42
|
||||
gh issue close 42 --reason "not planned"
|
||||
gh issue reopen 42
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# 关闭
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42 \
|
||||
-d '{"state": "closed", "state_reason": "completed"}'
|
||||
|
||||
# 重新开启
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42 \
|
||||
-d '{"state": "open"}'
|
||||
```
|
||||
|
||||
### 将 Issues 关联到 PR
|
||||
|
||||
当 PR 合并时,若 PR 正文中包含以下关键词,对应 issue 将自动关闭:
|
||||
|
||||
```
|
||||
Closes #42
|
||||
Fixes #42
|
||||
Resolves #42
|
||||
```
|
||||
|
||||
从 issue 创建分支:
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh issue develop 42 --checkout
|
||||
```
|
||||
|
||||
**使用 git(手动等效方式):**
|
||||
|
||||
```bash
|
||||
git checkout main && git pull origin main
|
||||
git checkout -b fix/issue-42-login-redirect
|
||||
```
|
||||
|
||||
## 4. Issue 分类工作流
|
||||
|
||||
当被要求对 issues 进行分类时:
|
||||
|
||||
1. **列出未分类的 issues:**
|
||||
|
||||
```bash
|
||||
# 使用 gh
|
||||
gh issue list --label "needs-triage" --state open
|
||||
|
||||
# 使用 curl
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?labels=needs-triage&state=open" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin):
|
||||
if 'pull_request' not in i:
|
||||
print(f\"#{i['number']} {i['title']}\")"
|
||||
```
|
||||
|
||||
2. **阅读并分类**每个 issue(查看详情,理解 bug 或功能需求)
|
||||
|
||||
3. **添加标签和优先级**(参见上方"管理 Issues"章节)
|
||||
|
||||
4. **分配负责人**(若归属明确)
|
||||
|
||||
5. **如有需要,添加分类说明评论**
|
||||
|
||||
## 5. 批量操作
|
||||
|
||||
对于批量操作,可将 API 调用与 shell 脚本结合使用:
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
# 关闭所有带特定标签的 issues
|
||||
gh issue list --label "wontfix" --json number --jq '.[].number' | \
|
||||
xargs -I {} gh issue close {} --reason "not planned"
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# 列出带某标签的 issue 编号,然后逐一关闭
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?labels=wontfix&state=open" \
|
||||
| python3 -c "import sys,json; [print(i['number']) for i in json.load(sys.stdin)]" \
|
||||
| while read num; do
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/$num \
|
||||
-d '{"state": "closed", "state_reason": "not_planned"}'
|
||||
echo "Closed #$num"
|
||||
done
|
||||
```
|
||||
|
||||
## 快速参考表
|
||||
|
||||
| 操作 | gh | curl 端点 |
|
||||
|--------|-----|--------------|
|
||||
| 列出 issues | `gh issue list` | `GET /repos/{o}/{r}/issues` |
|
||||
| 查看 issue | `gh issue view N` | `GET /repos/{o}/{r}/issues/N` |
|
||||
| 创建 issue | `gh issue create ...` | `POST /repos/{o}/{r}/issues` |
|
||||
| 添加标签 | `gh issue edit N --add-label ...` | `POST /repos/{o}/{r}/issues/N/labels` |
|
||||
| 分配 | `gh issue edit N --add-assignee ...` | `POST /repos/{o}/{r}/issues/N/assignees` |
|
||||
| 评论 | `gh issue comment N --body ...` | `POST /repos/{o}/{r}/issues/N/comments` |
|
||||
| 关闭 | `gh issue close N` | `PATCH /repos/{o}/{r}/issues/N` |
|
||||
| 搜索 | `gh issue list --search "..."` | `GET /search/issues?q=...` |
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
---
|
||||
title: "Github Pr Workflow — GitHub PR 生命周期:分支、提交、开启、CI、合并"
|
||||
sidebar_label: "Github Pr Workflow"
|
||||
description: "GitHub PR 生命周期:分支、提交、开启、CI、合并"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Pr Workflow
|
||||
|
||||
GitHub PR 生命周期:分支、提交、开启、CI、合并。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/github/github-pr-workflow` |
|
||||
| 版本 | `1.1.0` |
|
||||
| 作者 | Hermes Agent |
|
||||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `GitHub`, `Pull-Requests`, `CI/CD`, `Git`, `Automation`, `Merge` |
|
||||
| 相关 skill | [`github-auth`](/user-guide/skills/bundled/github/github-github-auth), [`github-code-review`](/user-guide/skills/bundled/github/github-github-code-review) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 agent 在 skill 激活时所看到的指令内容。
|
||||
:::
|
||||
|
||||
# GitHub Pull Request 工作流
|
||||
|
||||
管理 PR 生命周期的完整指南。每个章节优先展示 `gh` 方式,再给出适用于无 `gh` 环境的 `git` + `curl` 备用方案。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 已通过 GitHub 认证(参见 `github-auth` skill)
|
||||
- 位于含有 GitHub 远程仓库的 git 仓库中
|
||||
|
||||
### 快速认证检测
|
||||
|
||||
```bash
|
||||
# Determine which method to use throughout this workflow
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
AUTH="gh"
|
||||
else
|
||||
AUTH="git"
|
||||
# Ensure we have a token for API calls
|
||||
if [ -z "$GITHUB_TOKEN" ]; then
|
||||
if [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then
|
||||
GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo "Using: $AUTH"
|
||||
```
|
||||
|
||||
### 从 Git 远程地址提取 Owner/Repo
|
||||
|
||||
许多 `curl` 命令需要 `owner/repo`。从 git 远程地址中提取:
|
||||
|
||||
```bash
|
||||
# Works for both HTTPS and SSH remote URLs
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
||||
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
|
||||
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
|
||||
echo "Owner: $OWNER, Repo: $REPO"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 创建分支
|
||||
|
||||
此部分为纯 `git` 操作——两种方式完全相同:
|
||||
|
||||
```bash
|
||||
# Make sure you're up to date
|
||||
git fetch origin
|
||||
git checkout main && git pull origin main
|
||||
|
||||
# Create and switch to a new branch
|
||||
git checkout -b feat/add-user-authentication
|
||||
```
|
||||
|
||||
分支命名规范:
|
||||
- `feat/description` — 新功能
|
||||
- `fix/description` — 缺陷修复
|
||||
- `refactor/description` — 代码重构
|
||||
- `docs/description` — 文档
|
||||
- `ci/description` — CI/CD 变更
|
||||
|
||||
## 2. 提交变更
|
||||
|
||||
使用 agent 的文件工具(`write_file`、`patch`)进行修改,然后提交:
|
||||
|
||||
```bash
|
||||
# Stage specific files
|
||||
git add src/auth.py src/models/user.py tests/test_auth.py
|
||||
|
||||
# Commit with a conventional commit message
|
||||
git commit -m "feat: add JWT-based user authentication
|
||||
|
||||
- Add login/register endpoints
|
||||
- Add User model with password hashing
|
||||
- Add auth middleware for protected routes
|
||||
- Add unit tests for auth flow"
|
||||
```
|
||||
|
||||
提交信息格式(Conventional Commits):
|
||||
```
|
||||
type(scope): short description
|
||||
|
||||
Longer explanation if needed. Wrap at 72 characters.
|
||||
```
|
||||
|
||||
类型:`feat`、`fix`、`refactor`、`docs`、`test`、`ci`、`chore`、`perf`
|
||||
|
||||
## 3. 推送分支并创建 PR
|
||||
|
||||
### 推送分支(两种方式相同)
|
||||
|
||||
```bash
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
### 创建 PR
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh pr create \
|
||||
--title "feat: add JWT-based user authentication" \
|
||||
--body "## Summary
|
||||
- Adds login and register API endpoints
|
||||
- JWT token generation and validation
|
||||
|
||||
## Test Plan
|
||||
- [ ] Unit tests pass
|
||||
|
||||
Closes #42"
|
||||
```
|
||||
|
||||
选项:`--draft`、`--reviewer user1,user2`、`--label "enhancement"`、`--base develop`
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
BRANCH=$(git branch --show-current)
|
||||
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls \
|
||||
-d "{
|
||||
\"title\": \"feat: add JWT-based user authentication\",
|
||||
\"body\": \"## Summary\nAdds login and register API endpoints.\n\nCloses #42\",
|
||||
\"head\": \"$BRANCH\",
|
||||
\"base\": \"main\"
|
||||
}"
|
||||
```
|
||||
|
||||
响应 JSON 中包含 PR 的 `number`——请保存以供后续命令使用。
|
||||
|
||||
若要创建草稿 PR,在 JSON body 中添加 `"draft": true`。
|
||||
|
||||
## 4. 监控 CI 状态
|
||||
|
||||
### 检查 CI 状态
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
# One-shot check
|
||||
gh pr checks
|
||||
|
||||
# Watch until all checks finish (polls every 10s)
|
||||
gh pr checks --watch
|
||||
```
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
# Get the latest commit SHA on the current branch
|
||||
SHA=$(git rev-parse HEAD)
|
||||
|
||||
# Query the combined status
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
print(f\"Overall: {data['state']}\")
|
||||
for s in data.get('statuses', []):
|
||||
print(f\" {s['context']}: {s['state']} - {s.get('description', '')}\")"
|
||||
|
||||
# Also check GitHub Actions check runs (separate endpoint)
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/check-runs \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
for cr in data.get('check_runs', []):
|
||||
print(f\" {cr['name']}: {cr['status']} / {cr['conclusion'] or 'pending'}\")"
|
||||
```
|
||||
|
||||
### 轮询直至完成(git + curl)
|
||||
|
||||
```bash
|
||||
# Simple polling loop — check every 30 seconds, up to 10 minutes
|
||||
SHA=$(git rev-parse HEAD)
|
||||
for i in $(seq 1 20); do
|
||||
STATUS=$(curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['state'])")
|
||||
echo "Check $i: $STATUS"
|
||||
if [ "$STATUS" = "success" ] || [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
```
|
||||
|
||||
## 5. 自动修复 CI 失败
|
||||
|
||||
当 CI 失败时,进行诊断并修复。此循环适用于两种认证方式。
|
||||
|
||||
### 第一步:获取失败详情
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
# List recent workflow runs on this branch
|
||||
gh run list --branch $(git branch --show-current) --limit 5
|
||||
|
||||
# View failed logs
|
||||
gh run view <RUN_ID> --log-failed
|
||||
```
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
BRANCH=$(git branch --show-current)
|
||||
|
||||
# List workflow runs on this branch
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/actions/runs?branch=$BRANCH&per_page=5" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
runs = json.load(sys.stdin)['workflow_runs']
|
||||
for r in runs:
|
||||
print(f\"Run {r['id']}: {r['name']} - {r['conclusion'] or r['status']}\")"
|
||||
|
||||
# Get failed job logs (download as zip, extract, read)
|
||||
RUN_ID=<run_id>
|
||||
curl -s -L \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/logs \
|
||||
-o /tmp/ci-logs.zip
|
||||
cd /tmp && unzip -o ci-logs.zip -d ci-logs && cat ci-logs/*.txt
|
||||
```
|
||||
|
||||
### 第二步:修复并推送
|
||||
|
||||
定位问题后,使用文件工具(`patch`、`write_file`)进行修复:
|
||||
|
||||
```bash
|
||||
git add <fixed_files>
|
||||
git commit -m "fix: resolve CI failure in <check_name>"
|
||||
git push
|
||||
```
|
||||
|
||||
### 第三步:验证
|
||||
|
||||
使用第 4 节中的命令重新检查 CI 状态。
|
||||
|
||||
### 自动修复循环模式
|
||||
|
||||
当被要求自动修复 CI 时,遵循以下循环:
|
||||
|
||||
1. 检查 CI 状态 → 识别失败项
|
||||
2. 读取失败日志 → 理解错误原因
|
||||
3. 使用 `read_file` + `patch`/`write_file` → 修复代码
|
||||
4. `git add . && git commit -m "fix: ..." && git push`
|
||||
5. 等待 CI → 重新检查状态
|
||||
6. 若仍失败则重复(最多 3 次,之后询问用户)
|
||||
|
||||
## 6. 合并
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
# Squash merge + delete branch (cleanest for feature branches)
|
||||
gh pr merge --squash --delete-branch
|
||||
|
||||
# Enable auto-merge (merges when all checks pass)
|
||||
gh pr merge --auto --squash --delete-branch
|
||||
```
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
PR_NUMBER=<number>
|
||||
|
||||
# Merge the PR via API (squash)
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/merge \
|
||||
-d "{
|
||||
\"merge_method\": \"squash\",
|
||||
\"commit_title\": \"feat: add user authentication (#$PR_NUMBER)\"
|
||||
}"
|
||||
|
||||
# Delete the remote branch after merge
|
||||
BRANCH=$(git branch --show-current)
|
||||
git push origin --delete $BRANCH
|
||||
|
||||
# Switch back to main locally
|
||||
git checkout main && git pull origin main
|
||||
git branch -d $BRANCH
|
||||
```
|
||||
|
||||
合并方式:`"merge"`(合并提交)、`"squash"`、`"rebase"`
|
||||
|
||||
### 启用自动合并(curl)
|
||||
|
||||
```bash
|
||||
# Auto-merge requires the repo to have it enabled in settings.
|
||||
# This uses the GraphQL API since REST doesn't support auto-merge.
|
||||
PR_NODE_ID=$(curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['node_id'])")
|
||||
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/graphql \
|
||||
-d "{\"query\": \"mutation { enablePullRequestAutoMerge(input: {pullRequestId: \\\"$PR_NODE_ID\\\", mergeMethod: SQUASH}) { clientMutationId } }\"}"
|
||||
```
|
||||
|
||||
## 7. 完整工作流示例
|
||||
|
||||
```bash
|
||||
# 1. Start from clean main
|
||||
git checkout main && git pull origin main
|
||||
|
||||
# 2. Branch
|
||||
git checkout -b fix/login-redirect-bug
|
||||
|
||||
# 3. (Agent makes code changes with file tools)
|
||||
|
||||
# 4. Commit
|
||||
git add src/auth/login.py tests/test_login.py
|
||||
git commit -m "fix: correct redirect URL after login
|
||||
|
||||
Preserves the ?next= parameter instead of always redirecting to /dashboard."
|
||||
|
||||
# 5. Push
|
||||
git push -u origin HEAD
|
||||
|
||||
# 6. Create PR (picks gh or curl based on what's available)
|
||||
# ... (see Section 3)
|
||||
|
||||
# 7. Monitor CI (see Section 4)
|
||||
|
||||
# 8. Merge when green (see Section 6)
|
||||
```
|
||||
|
||||
## 常用 PR 命令参考
|
||||
|
||||
| 操作 | gh | git + curl |
|
||||
|--------|-----|-----------|
|
||||
| 列出我的 PR | `gh pr list --author @me` | `curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$OWNER/$REPO/pulls?state=open"` |
|
||||
| 查看 PR diff | `gh pr diff` | `git diff main...HEAD`(本地)或 `curl -H "Accept: application/vnd.github.diff" ...` |
|
||||
| 添加评论 | `gh pr comment N --body "..."` | `curl -X POST .../issues/N/comments -d '{"body":"..."}'` |
|
||||
| 请求审查 | `gh pr edit N --add-reviewer user` | `curl -X POST .../pulls/N/requested_reviewers -d '{"reviewers":["user"]}'` |
|
||||
| 关闭 PR | `gh pr close N` | `curl -X PATCH .../pulls/N -d '{"state":"closed"}'` |
|
||||
| 检出他人的 PR | `gh pr checkout N` | `git fetch origin pull/N/head:pr-N && git checkout pr-N` |
|
||||
+534
@@ -0,0 +1,534 @@
|
||||
---
|
||||
title: "Github 仓库管理 — 克隆/创建/fork 仓库;管理远程、发布"
|
||||
sidebar_label: "Github 仓库管理"
|
||||
description: "克隆/创建/fork 仓库;管理远程、发布"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github 仓库管理
|
||||
|
||||
克隆/创建/fork 仓库;管理远程、发布。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/github/github-repo-management` |
|
||||
| 版本 | `1.1.0` |
|
||||
| 作者 | Hermes Agent |
|
||||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `GitHub`, `Repositories`, `Git`, `Releases`, `Secrets`, `Configuration` |
|
||||
| 相关 skill | [`github-auth`](/user-guide/skills/bundled/github/github-github-auth), [`github-pr-workflow`](/user-guide/skills/bundled/github/github-github-pr-workflow), [`github-issues`](/user-guide/skills/bundled/github/github-github-issues) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 agent 在 skill 激活时所看到的指令内容。
|
||||
:::
|
||||
|
||||
# GitHub 仓库管理
|
||||
|
||||
创建、克隆、fork、配置和管理 GitHub 仓库。每个章节优先展示 `gh` 命令,然后是 `git` + `curl` 的备用方案。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 已通过 GitHub 认证(参见 `github-auth` skill)
|
||||
|
||||
### 初始化设置
|
||||
|
||||
```bash
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
AUTH="gh"
|
||||
else
|
||||
AUTH="git"
|
||||
if [ -z "$GITHUB_TOKEN" ]; then
|
||||
if [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then
|
||||
GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get your GitHub username (needed for several operations)
|
||||
if [ "$AUTH" = "gh" ]; then
|
||||
GH_USER=$(gh api user --jq '.login')
|
||||
else
|
||||
GH_USER=$(curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user | python3 -c "import sys,json; print(json.load(sys.stdin)['login'])")
|
||||
fi
|
||||
```
|
||||
|
||||
如果已在某个仓库内:
|
||||
|
||||
```bash
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
||||
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
|
||||
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 克隆仓库
|
||||
|
||||
克隆使用纯 `git` 命令——两种方式完全一致:
|
||||
|
||||
```bash
|
||||
# Clone via HTTPS (works with credential helper or token-embedded URL)
|
||||
git clone https://github.com/owner/repo-name.git
|
||||
|
||||
# Clone into a specific directory
|
||||
git clone https://github.com/owner/repo-name.git ./my-local-dir
|
||||
|
||||
# Shallow clone (faster for large repos)
|
||||
git clone --depth 1 https://github.com/owner/repo-name.git
|
||||
|
||||
# Clone a specific branch
|
||||
git clone --branch develop https://github.com/owner/repo-name.git
|
||||
|
||||
# Clone via SSH (if SSH is configured)
|
||||
git clone git@github.com:owner/repo-name.git
|
||||
```
|
||||
|
||||
**使用 gh(简写):**
|
||||
|
||||
```bash
|
||||
gh repo clone owner/repo-name
|
||||
gh repo clone owner/repo-name -- --depth 1
|
||||
```
|
||||
|
||||
## 2. 创建仓库
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
# Create a public repo and clone it
|
||||
gh repo create my-new-project --public --clone
|
||||
|
||||
# Private, with description and license
|
||||
gh repo create my-new-project --private --description "A useful tool" --license MIT --clone
|
||||
|
||||
# Under an organization
|
||||
gh repo create my-org/my-new-project --public --clone
|
||||
|
||||
# From existing local directory
|
||||
cd /path/to/existing/project
|
||||
gh repo create my-project --source . --public --push
|
||||
```
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
# Create the remote repo via API
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/user/repos \
|
||||
-d '{
|
||||
"name": "my-new-project",
|
||||
"description": "A useful tool",
|
||||
"private": false,
|
||||
"auto_init": true,
|
||||
"license_template": "mit"
|
||||
}'
|
||||
|
||||
# Clone it
|
||||
git clone https://github.com/$GH_USER/my-new-project.git
|
||||
cd my-new-project
|
||||
|
||||
# -- OR -- push an existing local directory to the new repo
|
||||
cd /path/to/existing/project
|
||||
git init
|
||||
git add .
|
||||
git commit -m "Initial commit"
|
||||
git remote add origin https://github.com/$GH_USER/my-new-project.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
在组织下创建:
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/orgs/my-org/repos \
|
||||
-d '{"name": "my-new-project", "private": false}'
|
||||
```
|
||||
|
||||
### 从模板创建
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh repo create my-new-app --template owner/template-repo --public --clone
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/owner/template-repo/generate \
|
||||
-d '{"owner": "'"$GH_USER"'", "name": "my-new-app", "private": false}'
|
||||
```
|
||||
|
||||
## 3. Fork 仓库
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh repo fork owner/repo-name --clone
|
||||
```
|
||||
|
||||
**使用 git + curl:**
|
||||
|
||||
```bash
|
||||
# Create the fork via API
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/owner/repo-name/forks
|
||||
|
||||
# Wait a moment for GitHub to create it, then clone
|
||||
sleep 3
|
||||
git clone https://github.com/$GH_USER/repo-name.git
|
||||
cd repo-name
|
||||
|
||||
# Add the original repo as "upstream" remote
|
||||
git remote add upstream https://github.com/owner/repo-name.git
|
||||
```
|
||||
|
||||
### 保持 Fork 同步
|
||||
|
||||
```bash
|
||||
# Pure git — works everywhere
|
||||
git fetch upstream
|
||||
git checkout main
|
||||
git merge upstream/main
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**使用 gh(快捷方式):**
|
||||
|
||||
```bash
|
||||
gh repo sync $GH_USER/repo-name
|
||||
```
|
||||
|
||||
## 4. 仓库信息
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh repo view owner/repo-name
|
||||
gh repo list --limit 20
|
||||
gh search repos "machine learning" --language python --sort stars
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# View repo details
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
r = json.load(sys.stdin)
|
||||
print(f\"Name: {r['full_name']}\")
|
||||
print(f\"Description: {r['description']}\")
|
||||
print(f\"Stars: {r['stargazers_count']} Forks: {r['forks_count']}\")
|
||||
print(f\"Default branch: {r['default_branch']}\")
|
||||
print(f\"Language: {r['language']}\")"
|
||||
|
||||
# List your repos
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/user/repos?per_page=20&sort=updated" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin):
|
||||
vis = 'private' if r['private'] else 'public'
|
||||
print(f\" {r['full_name']:40} {vis:8} {r.get('language', ''):10} ★{r['stargazers_count']}\")"
|
||||
|
||||
# Search repos
|
||||
curl -s \
|
||||
"https://api.github.com/search/repositories?q=machine+learning+language:python&sort=stars&per_page=10" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin)['items']:
|
||||
print(f\" {r['full_name']:40} ★{r['stargazers_count']:6} {r['description'][:60] if r['description'] else ''}\")"
|
||||
```
|
||||
|
||||
## 5. 仓库设置
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh repo edit --description "Updated description" --visibility public
|
||||
gh repo edit --enable-wiki=false --enable-issues=true
|
||||
gh repo edit --default-branch main
|
||||
gh repo edit --add-topic "machine-learning,python"
|
||||
gh repo edit --enable-auto-merge
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO \
|
||||
-d '{
|
||||
"description": "Updated description",
|
||||
"has_wiki": false,
|
||||
"has_issues": true,
|
||||
"allow_auto_merge": true
|
||||
}'
|
||||
|
||||
# Update topics
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.mercy-preview+json" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/topics \
|
||||
-d '{"names": ["machine-learning", "python", "automation"]}'
|
||||
```
|
||||
|
||||
## 6. 分支保护
|
||||
|
||||
```bash
|
||||
# View current protection
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/branches/main/protection
|
||||
|
||||
# Set up branch protection
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/branches/main/protection \
|
||||
-d '{
|
||||
"required_status_checks": {
|
||||
"strict": true,
|
||||
"contexts": ["ci/test", "ci/lint"]
|
||||
},
|
||||
"enforce_admins": false,
|
||||
"required_pull_request_reviews": {
|
||||
"required_approving_review_count": 1
|
||||
},
|
||||
"restrictions": null
|
||||
}'
|
||||
```
|
||||
|
||||
## 7. Secrets 管理(GitHub Actions)
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh secret set API_KEY --body "your-secret-value"
|
||||
gh secret set SSH_KEY < ~/.ssh/id_rsa
|
||||
gh secret list
|
||||
gh secret delete API_KEY
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
通过 API 设置 secret 需要使用仓库公钥加密——步骤较为繁琐:
|
||||
|
||||
```bash
|
||||
# Get the repo's public key for encrypting secrets
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/secrets/public-key
|
||||
|
||||
# Encrypt and set (requires Python with PyNaCl)
|
||||
python3 -c "
|
||||
from base64 import b64encode
|
||||
from nacl import encoding, public
|
||||
import json, sys
|
||||
|
||||
# Get the public key
|
||||
key_id = '<key_id_from_above>'
|
||||
public_key = '<base64_key_from_above>'
|
||||
|
||||
# Encrypt
|
||||
sealed = public.SealedBox(
|
||||
public.PublicKey(public_key.encode('utf-8'), encoding.Base64Encoder)
|
||||
).encrypt('your-secret-value'.encode('utf-8'))
|
||||
print(json.dumps({
|
||||
'encrypted_value': b64encode(sealed).decode('utf-8'),
|
||||
'key_id': key_id
|
||||
}))"
|
||||
|
||||
# Then PUT the encrypted secret
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/secrets/API_KEY \
|
||||
-d '<output from python script above>'
|
||||
|
||||
# List secrets (names only, values hidden)
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/secrets \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for s in json.load(sys.stdin)['secrets']:
|
||||
print(f\" {s['name']:30} updated: {s['updated_at']}\")"
|
||||
```
|
||||
|
||||
注意:对于 secret 管理,`gh secret set` 要简便得多。如果需要设置 secret 但 `gh` 不可用,建议仅为此操作安装它。
|
||||
|
||||
## 8. 发布(Releases)
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh release create v1.0.0 --title "v1.0.0" --generate-notes
|
||||
gh release create v2.0.0-rc1 --draft --prerelease --generate-notes
|
||||
gh release create v1.0.0 ./dist/binary --title "v1.0.0" --notes "Release notes"
|
||||
gh release list
|
||||
gh release download v1.0.0 --dir ./downloads
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# Create a release
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/releases \
|
||||
-d '{
|
||||
"tag_name": "v1.0.0",
|
||||
"name": "v1.0.0",
|
||||
"body": "## Changelog\n- Feature A\n- Bug fix B",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"generate_release_notes": true
|
||||
}'
|
||||
|
||||
# List releases
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/releases \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin):
|
||||
tag = r.get('tag_name', 'no tag')
|
||||
print(f\" {tag:15} {r['name']:30} {'draft' if r['draft'] else 'published'}\")"
|
||||
|
||||
# Upload a release asset (binary file)
|
||||
RELEASE_ID=<id_from_create_response>
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"https://uploads.github.com/repos/$OWNER/$REPO/releases/$RELEASE_ID/assets?name=binary-amd64" \
|
||||
--data-binary @./dist/binary-amd64
|
||||
```
|
||||
|
||||
## 9. GitHub Actions 工作流
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh workflow list
|
||||
gh run list --limit 10
|
||||
gh run view <RUN_ID>
|
||||
gh run view <RUN_ID> --log-failed
|
||||
gh run rerun <RUN_ID>
|
||||
gh run rerun <RUN_ID> --failed
|
||||
gh workflow run ci.yml --ref main
|
||||
gh workflow run deploy.yml -f environment=staging
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# List workflows
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/workflows \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for w in json.load(sys.stdin)['workflows']:
|
||||
print(f\" {w['id']:10} {w['name']:30} {w['state']}\")"
|
||||
|
||||
# List recent runs
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/actions/runs?per_page=10" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin)['workflow_runs']:
|
||||
print(f\" Run {r['id']} {r['name']:30} {r['conclusion'] or r['status']}\")"
|
||||
|
||||
# Download failed run logs
|
||||
RUN_ID=<run_id>
|
||||
curl -s -L \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/logs \
|
||||
-o /tmp/ci-logs.zip
|
||||
cd /tmp && unzip -o ci-logs.zip -d ci-logs
|
||||
|
||||
# Re-run a failed workflow
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/rerun
|
||||
|
||||
# Re-run only failed jobs
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/rerun-failed-jobs
|
||||
|
||||
# Trigger a workflow manually (workflow_dispatch)
|
||||
WORKFLOW_ID=<workflow_id_or_filename>
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/dispatches \
|
||||
-d '{"ref": "main", "inputs": {"environment": "staging"}}'
|
||||
```
|
||||
|
||||
## 10. Gists
|
||||
|
||||
**使用 gh:**
|
||||
|
||||
```bash
|
||||
gh gist create script.py --public --desc "Useful script"
|
||||
gh gist list
|
||||
```
|
||||
|
||||
**使用 curl:**
|
||||
|
||||
```bash
|
||||
# Create a gist
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/gists \
|
||||
-d '{
|
||||
"description": "Useful script",
|
||||
"public": true,
|
||||
"files": {
|
||||
"script.py": {"content": "print(\"hello\")"}
|
||||
}
|
||||
}'
|
||||
|
||||
# List your gists
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/gists \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for g in json.load(sys.stdin):
|
||||
files = ', '.join(g['files'].keys())
|
||||
print(f\" {g['id']} {g['description'] or '(no desc)':40} {files}\")"
|
||||
```
|
||||
|
||||
## 快速参考表
|
||||
|
||||
| 操作 | gh | git + curl |
|
||||
|--------|-----|-----------|
|
||||
| 克隆 | `gh repo clone o/r` | `git clone https://github.com/o/r.git` |
|
||||
| 创建仓库 | `gh repo create name --public` | `curl POST /user/repos` |
|
||||
| Fork | `gh repo fork o/r --clone` | `curl POST /repos/o/r/forks` + `git clone` |
|
||||
| 仓库信息 | `gh repo view o/r` | `curl GET /repos/o/r` |
|
||||
| 编辑设置 | `gh repo edit --...` | `curl PATCH /repos/o/r` |
|
||||
| 创建发布 | `gh release create v1.0` | `curl POST /repos/o/r/releases` |
|
||||
| 列出工作流 | `gh workflow list` | `curl GET /repos/o/r/actions/workflows` |
|
||||
| 重跑 CI | `gh run rerun ID` | `curl POST /repos/o/r/actions/runs/ID/rerun` |
|
||||
| 设置 secret | `gh secret set KEY` | `curl PUT /repos/o/r/actions/secrets/KEY`(需加密) |
|
||||
Reference in New Issue
Block a user