贡献指南¶
感谢您有兴趣为 clinvoker 做贡献!本指南涵盖开发环境设置、项目结构、编码标准、测试要求和贡献流程。
开发环境设置¶
前置要求¶
- Go 1.24 或更高版本
- Git
- (可选)Nix 用于可复现环境
- (可选)golangci-lint 用于代码检查
- (可选)pre-commit 用于 git 钩子
Fork 和克隆¶
# 在 GitHub 上 fork 仓库,然后:
git clone https://github.com/YOUR_USERNAME/clinvoker.git
cd clinvoker
git remote add upstream https://github.com/signalridge/clinvoker.git
使用 Nix(推荐)¶
这会在可复现环境中提供所有必需的工具。
手动设置¶
预提交钩子¶
项目结构¶
clinvoker/
├── cmd/
│ └── clinvk/ # 主应用入口点
│ └── main.go
├── internal/
│ ├── app/ # CLI 命令实现
│ ├── backend/ # 后端抽象层
│ ├── config/ # 配置管理
│ ├── executor/ # 命令执行
│ ├── output/ # 输出格式化
│ ├── server/ # HTTP API 服务器
│ ├── session/ # 会话管理
│ ├── auth/ # API 密钥管理
│ ├── metrics/ # Prometheus 指标
│ └── resilience/ # 熔断器
├── docs/ # 文档
├── scripts/ # 构建和实用脚本
└── test/ # 集成测试
包职责¶
| 包 | 用途 | 关键文件 |
|---|---|---|
app/ |
使用 Cobra 的 CLI 命令 | app.go, cmd_*.go |
backend/ |
后端抽象 | backend.go, registry.go, claude.go, codex.go, gemini.go |
config/ |
基于 Viper 的配置 | config.go, validate.go |
executor/ |
子进程执行 | executor.go, signal.go |
server/ |
HTTP 服务器 | server.go, routes.go, handlers/, middleware/ |
session/ |
会话持久化 | session.go, store.go, filelock.go |
编码标准¶
Go 指南¶
遵循 Effective Go 和 Google Go 风格指南:
- 格式化:使用
gofmt或goimports - 代码检查:提交前运行
golangci-lint run - 命名:使用描述性、惯用的名称
- 注释:为所有导出的类型和函数添加文档
- 错误处理:用上下文包装错误,避免裸返回
代码风格示例¶
// 良好:清晰的函数名、适当的文档、错误处理
// ExecuteCommand 运行给定的命令并返回输出。
func ExecuteCommand(ctx context.Context, cfg *Config, cmd *exec.Cmd) (*Result, error) {
if cfg == nil {
return nil, fmt.Errorf("config is required")
}
// 实现
result, err := runWithTimeout(ctx, cmd, cfg.Timeout)
if err != nil {
return nil, fmt.Errorf("failed to execute command: %w", err)
}
return result, nil
}
// 不良:不清晰的名称、缺少文档、错误的错误处理
func exec(cfg *Config, c *exec.Cmd) (*Result, error) {
res, _ := runWithTimeout(context.Background(), c, cfg.Timeout)
return res, nil
}
错误处理¶
使用项目的错误包进行一致的错误处理:
import apperrors "github.com/signalridge/clinvoker/internal/errors"
// 创建带上下文的错误
return apperrors.BackendError("claude", err)
// 检查错误类型
if apperrors.IsCode(err, apperrors.ErrCodeBackendUnavailable) {
// 处理特定错误
}
测试标准¶
所有代码都必须有测试。遵循以下指南:
- 文件命名:与源文件一起的
*_test.go - 表驱动测试:用于多个测试用例
- 并行测试:对独立测试使用
t.Parallel() - 覆盖率:新代码目标 >80% 覆盖率
- 模拟:使用接口实现可测试性
func TestExecuteCommand(t *testing.T) {
t.Parallel()
tests := []struct {
name string
cfg *Config
cmd *exec.Cmd
wantErr bool
}{
{
name: "valid command",
cfg: &Config{Timeout: 30 * time.Second},
cmd: exec.Command("echo", "hello"),
wantErr: false,
},
{
name: "nil config",
cfg: nil,
cmd: exec.Command("echo", "hello"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
_, err := ExecuteCommand(ctx, tt.cfg, tt.cmd)
if (err != nil) != tt.wantErr {
t.Errorf("ExecuteCommand() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
测试要求¶
运行测试¶
# 所有测试
go test ./...
# 带竞态检测
go test -race ./...
# 带覆盖率
go test -coverprofile=coverage.txt ./...
go tool cover -html=coverage.txt -o coverage.html
# 仅短测试
go test -short ./...
# 特定包
go test ./internal/backend/...
集成测试¶
集成测试需要安装实际的后端 CLI:
# 运行集成测试
CLINVK_TEST_INTEGRATION=1 go test ./test/...
# 使用特定后端运行
CLINVK_TEST_BACKEND=claude go test ./test/...
基准测试¶
文档要求¶
代码注释¶
为所有导出的类型和函数添加文档:
// Backend 表示一个 AI CLI 后端。
type Backend interface {
// Name 返回后端标识符。
Name() string
// IsAvailable 检查后端 CLI 是否已安装。
IsAvailable() bool
}
// NewStore 在给定目录创建新的会话存储。
// 如果目录不存在则创建它。
func NewStore(dir string) (*Store, error) {
// ...
}
用户文档¶
为用户可见的更改更新文档:
- 概念:如果设计更改则更新架构文档
- 指南:为新功能添加/更新操作指南
- 参考:为更改更新 API/CLI 参考
- 变更日志:添加条目到 CHANGELOG.md
文档风格¶
- 使用清晰、简洁的语言
- 包含代码示例
- 为复杂概念添加图表(Mermaid)
- 保持中文和英文版本同步
PR 流程¶
分支命名¶
使用约定的分支名称:
| 前缀 | 用途 | 示例 |
|---|---|---|
feat/ |
新功能 | feat/add-gemini-backend |
fix/ |
Bug 修复 | fix/session-locking |
docs/ |
文档 | docs/api-examples |
refactor/ |
代码重构 | refactor/executor |
test/ |
测试添加 | test/backend-coverage |
chore/ |
维护 | chore/update-deps |
提交消息约定¶
类型:feat, fix, docs, style, refactor, test, chore
示例:
feat(backend): add support for new AI provider
添加对新 XYZ AI CLI 工具的支持。这包括:
- 后端实现
- 标志映射
- 文档更新
fix(session): handle concurrent access correctly
修复当多个进程尝试同时更新同一会话时
会话存储中的竞态条件。
docs(readme): update installation instructions
test(backend): add unit tests for registry
chore(deps): update golang.org/x packages
Pull Request 模板¶
## 摘要
更改的简要描述。
## 更改
- 更改 1
- 更改 2
## 测试计划
如何测试更改:
- [ ] 单元测试通过
- [ ] 集成测试通过
- [ ] 执行了手动测试
## 相关 Issues
Fixes #123
Closes #456
代码审查清单¶
提交 PR 前:
- [ ] 所有测试在本地通过
- [ ] 代码遵循风格指南
- [ ] 文档已更新
- [ ] 提交消息遵循约定
- [ ] 没有包含不必要的文件
- [ ] CHANGELOG.md 已更新(如适用)
审查流程¶
- PR 需要至少一个批准
- CI 必须在合并前通过
- 及时解决审查意见
- 保持 PR 专注且大小适中(首选 <500 行)
- 如果有冲突则变基到 main
发布流程¶
版本控制¶
clinvoker 遵循 语义化版本:
MAJOR:破坏性更改MINOR:新功能,向后兼容PATCH:错误修复,向后兼容
使用 release-please 自动发布¶
我们使用 release-please 进行自动化版本管理和变更日志生成。
工作原理¶
- 编写代码 使用 Conventional Commits
- 推送到 main - release-please 自动创建/更新 Release PR
- 合并 Release PR - 创建 git 标签和 GitHub Release
- GoReleaser 触发 - 为所有平台构建二进制文件
Conventional Commits 与版本控制¶
| 提交类型 | 版本变更 | 变更日志分类 |
|---|---|---|
feat: |
Minor (0.1.0 → 0.2.0) | 新功能 |
fix: |
Patch (0.1.0 → 0.1.1) | Bug 修复 |
feat!: 或 BREAKING CHANGE: |
Major (0.1.0 → 1.0.0) | 破坏性变更 |
perf: |
Patch | 性能优化 |
refactor: |
Patch | 重构 |
deps: |
Patch | 依赖更新 |
security: |
Patch | 安全修复 |
docs:, chore:, test:, ci: |
不发布 | 隐藏 |
工作流示例¶
# 1. 创建功能分支
git checkout -b feat/new-feature
# 2. 使用约定格式提交更改
git commit -m "feat(backend): add support for new AI provider"
# 3. 创建 PR 并合并到 main
gh pr create && gh pr merge
# 4. release-please 自动创建 Release PR
# 标题: "chore: release v0.2.0"
# 内容: 更新的 CHANGELOG.md 和版本号
# 5. 审查并合并 Release PR
# → 创建标签 v0.2.0
# → GoReleaser 构建并发布
# 6. 完成!二进制文件可通过以下方式获取:
# - GitHub Releases
# - Homebrew: brew install signalridge/tap/clinvk
# - go install: go install github.com/signalridge/clinvoker/cmd/clinvk@latest
手动发布(仅紧急情况)¶
当自动化不可用时的紧急发布:
# 1. 手动更新 CHANGELOG.md
# 2. 创建并推送标签
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3
# 3. GoReleaser 工作流自动触发
问题?¶
- 为 bug 或功能开 issue
- 为问题开讨论
- 创建新 issue 前先检查现有的
行为准则¶
参与即表示您同意:
- 尊重和包容
- 优雅地接受建设性批评
- 专注于对社区最有利的事情
- 对他人表示同理心