Files
DevPack/docs/DEVELOPMENT.md
2026-03-03 18:20:18 +08:00

639 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DevPack 开发指南
> 版本v1.0.0-draft
> 更新日期2026-03-03
> 作者DevPack Team
---
## 1. 开发环境搭建
### 1.1 前置要求
| 工具 | 最低版本 | 安装方式 |
|------|---------|---------|
| Go | 1.22+ | [golang.org](https://golang.org/dl/) |
| Git | 2.30+ | [git-scm.com](https://git-scm.com/) |
| Make | 3.81+ | 系统包管理器Windows 推荐用 `choco install make` |
| golangci-lint | 1.55+ | `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` |
### 1.2 获取代码
```bash
git clone https://github.com/user/devpack.git
cd devpack
```
### 1.3 安装依赖
```bash
go mod download
```
### 1.4 验证环境
```bash
# 运行测试
go test ./...
# 构建
go build -o devpack ./cmd/devpack/
# 代码检查
golangci-lint run
```
---
## 2. 项目结构详解
```
DevPack/
├── cmd/ # 应用入口
│ └── devpack/
│ ├── main.go # 主入口
│ └── commands/ # CLI 命令
│ ├── root.go # 根命令
│ ├── init.go # devpack init
│ ├── scan.go # devpack scan
│ ├── capture.go # devpack capture
│ ├── restore.go # devpack restore
│ ├── diff.go # devpack diff
│ ├── export.go # devpack export
│ ├── import.go # devpack import
│ ├── list.go # devpack list
│ ├── profile.go # devpack profile
│ └── version.go # devpack version
├── internal/ # 内部包
│ ├── collector/ # 采集器核心
│ │ ├── collector.go # Collector 接口定义
│ │ ├── registry.go # 采集器注册中心
│ │ ├── options.go # 扫描/捕获/还原选项
│ │ ├── result.go # 结果类型定义
│ │ │
│ │ ├── runtime/ # 运行时采集器
│ │ │ ├── go.go # Go 采集器
│ │ │ ├── node.go # Node.js 采集器
│ │ │ ├── python.go # Python 采集器
│ │ │ ├── java.go # Java 采集器
│ │ │ ├── rust.go # Rust 采集器
│ │ │ ├── dotnet.go # .NET 采集器
│ │ │ └── runtime_test.go # 运行时采集器测试
│ │ │
│ │ ├── editor/ # 编辑器采集器
│ │ │ ├── vscode.go # VS Code 采集器
│ │ │ ├── jetbrains.go # JetBrains 采集器
│ │ │ ├── vim.go # Vim/Neovim 采集器
│ │ │ └── editor_test.go
│ │ │
│ │ ├── shell/ # Shell 采集器
│ │ │ ├── powershell.go # PowerShell 采集器
│ │ │ ├── bash.go # Bash 采集器
│ │ │ ├── zsh.go # Zsh 采集器
│ │ │ ├── fish.go # Fish 采集器
│ │ │ ├── terminal.go # 终端模拟器配置Windows Terminal 等)
│ │ │ └── shell_test.go
│ │ │
│ │ ├── pkgmgr/ # 包管理器采集器
│ │ │ ├── scoop.go # Scoop 采集器
│ │ │ ├── chocolatey.go # Chocolatey 采集器
│ │ │ ├── winget.go # Winget 采集器
│ │ │ ├── homebrew.go # Homebrew 采集器
│ │ │ ├── apt.go # APT 采集器
│ │ │ └── pkgmgr_test.go
│ │ │
│ │ ├── git/ # Git 配置采集器
│ │ │ ├── gitconfig.go
│ │ │ └── git_test.go
│ │ │
│ │ ├── env/ # 环境变量采集器
│ │ │ ├── envvar.go
│ │ │ └── env_test.go
│ │ │
│ │ ├── font/ # 字体采集器
│ │ │ ├── font.go
│ │ │ └── font_test.go
│ │ │
│ │ └── ssh/ # SSH/GPG 采集器
│ │ ├── ssh.go
│ │ └── ssh_test.go
│ │
│ ├── pack/ # 打包引擎
│ │ ├── packer.go # 打包器
│ │ ├── unpacker.go # 解包器
│ │ ├── format.go # Pack 格式定义
│ │ └── pack_test.go
│ │
│ ├── restore/ # 还原引擎
│ │ ├── engine.go # 还原核心逻辑
│ │ ├── planner.go # 还原计划生成
│ │ ├── conflict.go # 冲突检测与处理
│ │ ├── rollback.go # 回滚管理
│ │ ├── verifier.go # 还原验证
│ │ └── restore_test.go
│ │
│ ├── profile/ # 配置文件管理
│ │ ├── profile.go # Profile 定义与操作
│ │ ├── parser.go # YAML 解析
│ │ └── profile_test.go
│ │
│ ├── diff/ # 差异对比
│ │ ├── differ.go # 差异计算
│ │ ├── renderer.go # 差异展示
│ │ └── diff_test.go
│ │
│ ├── crypto/ # 加密模块
│ │ ├── aesgcm.go # AES-256-GCM 实现
│ │ ├── argon2.go # Argon2id 密钥派生
│ │ └── crypto_test.go
│ │
│ ├── platform/ # 平台适配层
│ │ ├── platform.go # Platform 接口
│ │ ├── detect.go # 平台检测
│ │ ├── windows.go # Windows 实现
│ │ ├── darwin.go # macOS 实现
│ │ ├── linux.go # Linux 实现
│ │ └── platform_test.go
│ │
│ ├── config/ # 应用配置
│ │ ├── config.go # 全局配置
│ │ └── paths.go # 路径管理
│ │
│ ├── ui/ # 终端 UI
│ │ ├── printer.go # 格式化输出
│ │ ├── progress.go # 进度条
│ │ ├── table.go # 表格输出
│ │ └── prompt.go # 用户交互
│ │
│ └── logger/ # 日志
│ └── logger.go # 日志初始化
├── pkg/ # 公共包
│ ├── manifest/ # Pack 清单
│ │ ├── manifest.go # Manifest 结构定义
│ │ └── validate.go # Manifest 验证
│ └── version/ # 版本信息
│ └── version.go # 版本号管理
├── plugins/ # 外部插件目录
│ └── README.md # 插件开发指南
├── test/ # 集成与端到端测试
│ ├── e2e/ # 端到端测试
│ │ ├── capture_restore_test.go
│ │ └── testdata/ # 测试数据
│ └── fixtures/ # 测试 fixtures
│ └── sample-pack/
├── scripts/ # 脚本
│ ├── build.sh # 构建脚本
│ ├── install.sh # 安装脚本
│ └── release.sh # 发布脚本
├── .github/
│ └── workflows/
│ ├── ci.yml # CI 流水线
│ └── release.yml # 发布流水线
├── .golangci.yml # Lint 配置
├── .gitignore
├── go.mod
├── go.sum
├── Makefile
├── LICENSE
└── README.md
```
---
## 3. 编码规范
### 3.1 Go 编码规范
遵循 [Effective Go](https://golang.org/doc/effective_go) 和 [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)。
#### 命名规范
```go
// ✅ 包名:小写单词,不用下划线
package collector
package pkgmgr
// ✅ 接口名:动词或名词,单方法接口用 -er 后缀
type Collector interface { ... }
type Scanner interface { ... }
// ✅ 导出函数/方法:大驼峰
func NewRegistry() *Registry { ... }
func (r *Registry) Register(c Collector) error { ... }
// ✅ 非导出函数/方法:小驼峰
func parseVersion(raw string) (string, error) { ... }
// ✅ 常量:大驼峰(导出)或小驼峰(非导出)
const DefaultTimeout = 30 * time.Second
const maxRetries = 3
// ✅ 错误变量Err 前缀
var ErrCollectorNotFound = errors.New("collector not found")
var ErrPackCorrupted = errors.New("pack file corrupted")
```
#### 错误处理
```go
// ✅ 使用 fmt.Errorf 包装错误,提供上下文
func (c *GoCollector) Scan(ctx context.Context) (*ScanResult, error) {
version, err := c.detectVersion(ctx)
if err != nil {
return nil, fmt.Errorf("go collector: detect version: %w", err)
}
// ...
}
// ✅ 自定义错误类型用于需要类型判断的场景
type CollectorError struct {
Collector string
Op string
Err error
}
func (e *CollectorError) Error() string {
return fmt.Sprintf("%s: %s: %v", e.Collector, e.Op, e.Err)
}
func (e *CollectorError) Unwrap() error {
return e.Err
}
```
#### Context 使用
```go
// ✅ 第一个参数传递 context
func (c *GoCollector) Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error) {
// 使用 context 控制超时
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "go", "version")
// ...
}
```
### 3.2 项目约定
| 约定 | 说明 |
|------|------|
| 文件命名 | 小写 + 下划线:`go_collector.go`, `scan_result.go` |
| 测试文件 | `*_test.go` 放在同一包下 |
| 平台特定 | 使用 Go build tag`//go:build windows` |
| 依赖注入 | 通过接口 + 构造函数实现 |
| 配置 | 使用 Viper支持文件 + 环境变量 + 命令行参数 |
| 日志 | 使用 zerolog结构化日志 |
### 3.3 Git 提交规范
使用 [Conventional Commits](https://www.conventionalcommits.org/)
```
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
```
**类型 (type)**
| Type | 说明 |
|------|------|
| `feat` | 新功能 |
| `fix` | Bug 修复 |
| `docs` | 文档 |
| `style` | 代码格式 |
| `refactor` | 重构 |
| `perf` | 性能优化 |
| `test` | 测试 |
| `chore` | 构建/工具 |
**Scope 示例:** `collector`, `pack`, `restore`, `cli`, `platform`, `crypto`
**示例:**
```
feat(collector): add VS Code extension collector
Scan and capture VS Code extensions list and settings.
Closes #42
```
---
## 4. 开发工作流
### 4.1 分支策略
```
main (稳定发布)
├── develop (开发主干)
│ │
│ ├── feature/collector-vscode (功能分支)
│ ├── feature/restore-engine (功能分支)
│ └── fix/scan-timeout (修复分支)
└── release/v0.1.0 (发布分支)
```
### 4.2 开发流程
1. **创建分支:** `git checkout -b feature/xxx develop`
2. **开发 & 测试:** 编写代码 + 单元测试
3. **代码检查:** `make lint`
4. **提交:** 遵循 Conventional Commits
5. **推送 & PR** 推送分支,创建 Pull Request
6. **Code Review** 至少一人 Review 通过
7. **合并:** Squash Merge 到 develop
### 4.3 常用 Make 命令
```makefile
# 构建
make build # 构建当前平台
make build-all # 构建所有平台
make build-windows # 构建 Windows 版本
make build-darwin # 构建 macOS 版本
make build-linux # 构建 Linux 版本
# 测试
make test # 运行所有测试
make test-unit # 运行单元测试
make test-integration # 运行集成测试
make test-e2e # 运行端到端测试
make test-coverage # 生成覆盖率报告
# 代码质量
make lint # 运行 golangci-lint
make fmt # 格式化代码
make vet # go vet 检查
# 开发
make run # 编译并运行
make dev # 开发模式(热重载)
make clean # 清理构建产物
# 发布
make release # 使用 GoReleaser 发布
make snapshot # GoReleaser 快照(不发布)
```
---
## 5. 添加新采集器指南
### 5.1 步骤
以添加 "Docker" 采集器为例:
#### Step 1: 创建采集器文件
```go
// internal/collector/runtime/docker.go
package runtime
import (
"context"
"github.com/user/devpack/internal/collector"
)
// DockerCollector Docker 环境采集器
type DockerCollector struct{}
// 确保实现了 Collector 接口
var _ collector.Collector = (*DockerCollector)(nil)
func NewDockerCollector() *DockerCollector {
return &DockerCollector{}
}
func (c *DockerCollector) Name() string { return "docker" }
func (c *DockerCollector) DisplayName() string { return "Docker" }
func (c *DockerCollector) Description() string {
return "Captures Docker version, images, and configuration"
}
func (c *DockerCollector) Category() collector.Category {
return collector.CategoryRuntime
}
func (c *DockerCollector) IsAvailable(ctx context.Context) bool {
_, err := exec.LookPath("docker")
return err == nil
}
func (c *DockerCollector) Scan(ctx context.Context, opts collector.ScanOptions) (*collector.ScanResult, error) {
// 实现扫描逻辑
// 1. 检测 Docker 版本
// 2. 列出本地镜像
// 3. 获取 Docker daemon 配置
return nil, nil
}
func (c *DockerCollector) Capture(ctx context.Context, targetDir string, opts collector.CaptureOptions) error {
// 实现捕获逻辑
return nil
}
func (c *DockerCollector) Restore(ctx context.Context, sourceDir string, opts collector.RestoreOptions) error {
// 实现还原逻辑
return nil
}
func (c *DockerCollector) Verify(ctx context.Context) (*collector.VerifyResult, error) {
// 实现验证逻辑
return nil, nil
}
```
#### Step 2: 注册采集器
```go
// internal/collector/registry.go 中添加注册
func NewDefaultRegistry() *Registry {
r := NewRegistry()
// Runtime collectors
r.Register(runtime.NewGoCollector())
r.Register(runtime.NewNodeCollector())
r.Register(runtime.NewDockerCollector()) // 新增
// ...
return r
}
```
#### Step 3: 编写测试
```go
// internal/collector/runtime/docker_test.go
func TestDockerCollector_Scan(t *testing.T) {
c := NewDockerCollector()
if !c.IsAvailable(context.Background()) {
t.Skip("Docker not available")
}
result, err := c.Scan(context.Background(), collector.ScanOptions{})
assert.NoError(t, err)
assert.NotEmpty(t, result.Items)
}
```
#### Step 4: 更新文档
在 README.md 和 PRD.md 中添加 Docker 采集器的说明。
---
## 6. 调试指南
### 6.1 日志级别
```bash
# 设置日志级别
DEVPACK_LOG_LEVEL=debug devpack scan
# 或通过命令行参数
devpack scan --log-level debug
```
| 级别 | 用途 |
|------|------|
| `trace` | 非常详细的调试信息 |
| `debug` | 调试信息 |
| `info` | 正常操作信息(默认) |
| `warn` | 警告 |
| `error` | 错误 |
| `fatal` | 致命错误 |
### 6.2 VS Code 调试配置
```json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug DevPack Scan",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["scan", "--verbose"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
},
{
"name": "Debug DevPack Capture",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/devpack",
"args": ["capture", "--name", "test-env"],
"env": {
"DEVPACK_LOG_LEVEL": "debug"
}
}
]
}
```
### 6.3 常见问题排查
| 问题 | 排查方向 |
|------|---------|
| 采集器找不到工具 | 检查 PATH 环境变量,确认工具已安装 |
| Pack 文件损坏 | 使用 `devpack verify <pack>` 校验 |
| 还原权限不足 | 以管理员身份运行 |
| 跨版本不兼容 | 检查 Manifest 中的 format_ver |
---
## 7. 发布流程
### 7.1 版本号规范
遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)
```
MAJOR.MINOR.PATCH[-PRERELEASE]
- MAJOR: 不兼容的 API 变更
- MINOR: 向下兼容的功能新增
- PATCH: 向下兼容的 Bug 修复
```
### 7.2 发布步骤
```bash
# 1. 确保所有测试通过
make test
# 2. 更新 CHANGELOG
# 编辑 CHANGELOG.md
# 3. 创建 Release Tag
git tag -a v0.1.0 -m "Release v0.1.0"
git push origin v0.1.0
# 4. GoReleaser 自动构建发布CI 触发)
# 或手动:
make release
```
### 7.3 GoReleaser 配置
```yaml
# .goreleaser.yml
version: 2
builds:
- main: ./cmd/devpack
binary: devpack
env:
- CGO_ENABLED=0
goos:
- windows
- darwin
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X github.com/user/devpack/pkg/version.Version={{.Version}}
- -X github.com/user/devpack/pkg/version.Commit={{.Commit}}
- -X github.com/user/devpack/pkg/version.Date={{.Date}}
archives:
- format: tar.gz
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^chore:'
```