- 机器输出(json/yaml)只写 stdout;日志/调试信息只写 stderr。
- 核心逻辑放
internal/*,CLI 仅做参数解析与输出。 - 为命令/输出/错误码维护稳定契约(AI-first)。
- CLI:cobra
- Config:自研(支持 CLI > ENV > Config 优先级合并)
- DB:database/sql + go-sql-driver/mysql + jackc/pgx
- Secrets:zalando/go-keyring
- SSH:x/crypto/ssh
- Output:json/yaml/csv + text/tabwriter
# 构建
go build -o xsql ./cmd/xsql
# 运行单元测试
go test ./...
# 运行测试(带覆盖率)
go test -cover ./...
# 运行 E2E 测试
go test -tags=e2e ./tests/e2e/...
# 导出 spec
./xsql spec --format json详细测试指南见 docs/testing.md
集成测试需要 MySQL 和 PostgreSQL 数据库。使用 docker-compose 启动:
# 启动数据库
docker-compose up -d
# 等待数据库就绪(约 10-20 秒)
docker-compose ps
# 运行集成测试
XSQL_TEST_MYSQL_DSN="root:root@tcp(127.0.0.1:3306)/testdb?parseTime=true" \
XSQL_TEST_PG_DSN="postgres://postgres:postgres@127.0.0.1:5432/testdb?sslmode=disable" \
go test -v -tags=integration ./tests/integration/...
# 停止数据库
docker-compose down# 启动数据库
docker-compose up -d
# 运行集成测试
$env:XSQL_TEST_MYSQL_DSN="root:root@tcp(127.0.0.1:3306)/testdb?parseTime=true"
$env:XSQL_TEST_PG_DSN="postgres://postgres:postgres@127.0.0.1:5432/testdb?sslmode=disable"
go test -v -tags=integration ./tests/integration/...
# 停止数据库
docker-compose downMySQL 测试:
- 基本查询(SELECT)
- 多行结果、空结果
- 数据类型(int, float, string, bool, datetime)
- SHOW DATABASES、EXPLAIN
- 只读策略(INSERT/UPDATE/DELETE/DROP/CREATE 拦截)
- 无效 SQL 错误处理
PostgreSQL 测试:
- 基本查询
- generate_series、CTE
- 系统表查询(pg_database)
- 只读策略
- JSON 数据类型
cmd/xsql/ # CLI 入口
internal/
app/ # 应用编排
config/ # 配置加载/合并/校验 + profile
db/ # driver registry + query
mysql/ # MySQL driver
pg/ # PostgreSQL driver
errors/ # 错误码/退出码
log/ # slog 日志
mcp/ # MCP Server 实现
output/ # json/yaml/table/csv 输出
proxy/ # 端口转发代理
secret/ # keyring + 明文策略
spec/ # tool spec 导出
ssh/ # SSH client + dial
tests/
e2e/ # E2E 测试
integration/ # 集成测试(需要数据库)
docs/ # 文档
- 在
internal/db/<name>/创建 driver 实现 - 实现
db.Driver接口 - 在
init()中调用db.Register("<name>", &Driver{}) - 在
cmd/xsql/main.go中 import 触发注册
项目使用 GitHub Actions 进行 CI:
- 单元测试(所有平台)
- 集成测试(MySQL 8.0 + PostgreSQL 16)
- 代码检查(golangci-lint)
- 代码覆盖率上报
详见 .github/workflows/ci.yml。