news 2026/9/13 11:04:15

基于 Keploy AGENTS.md 的工程实践指南:构建、运行、代码约定与 CI 体系全解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 Keploy AGENTS.md 的工程实践指南:构建、运行、代码约定与 CI 体系全解

基于 Keploy AGENTS.md 的工程实践指南:构建、运行、代码约定与 CI 体系全解

【免费下载链接】keployOpen-source platform for creating safe, isolated production sandboxes for API, integration, and E2E testing.项目地址: https://gitcode.com/GitHub_Trending/ke/keploy

本文以仓库根目录的 AGENTS.md 为主体骨架,完整梳理 Keploy 这个"录制-重放"式后端测试工具的工程参考:如何构建与运行keploy单二进制 CLI、各平台的流量拦截差异、用户可见命令速查、磁盘产物格式、仓库特有的 Go 编码约定,以及 CI 矩阵如何保证 mock 格式的前后兼容。读完你不仅能按文档独立搭建、运行 Keploy,还能对照源码定位常见改动的入口目录。

项目定位与入口

AGENTS.md 对项目本身的定义是:Keploy 是一个后端测试工具,它从运行中的应用录制真实 API + 依赖流量,再以 mock 重放为确定性测试。其关键工程特征有三点:

  • 流量拦截发生在网络层:Linux 上通过 eBPF 实现(当前树中生成代码位于 pkg/agent/hooks/linux/),macOS/Windows 走用户态代理路径,因此应用无需引入 SDK、无需改代码
  • 产物是一个 Go 编写的单二进制 CLI:keploy
  • Go 模块为go.keploy.io/server/v3,主入口是 main.go,其中start()最终调用 cli/root.go 中的cli.Root(...)构建 cobra 根命令并执行所有已注册子命令。

AGENTS.md 开篇还确立了一条工作原则:"这里的一切都以仓库当前状态为准——如果下文与磁盘上的内容冲突,相信磁盘,并更新此文件"。后文会看到这条原则在本仓库中有真实体现(例如生成代码路径、lint 排除项的漂移)。

构建:必须带viper_bind_struct标签

文档给出的两条标准构建命令:

# 标准构建(CI 中称为 "build-no-race") go build -tags=viper_bind_struct -o keploy . # 开启竞态检测的构建(CI 中称为 "build",是 CI 矩阵的默认构建) CGO_ENABLED=1 go build -race -tags=viper_bind_struct -o keploy .

要点:

  • viper_bind_struct构建标签是必需的。AGENTS.md 明确警告:漏掉该标签会导致运行时配置字段无法正确绑定(config.Config与 CLI flag 的绑定依赖此 tag 下的 viper 编译变体)。
  • 版本、Sentry DSN、服务端 URL、GitHub client ID 通过-ldflags注入,参考 Dockerfile 与 goreleaser.yaml。在 main.go 中可以看到对应的接收端:包级变量versiondsnsetVersion()兜底——本地未注入时version默认为3-dev

本地运行:平台支持矩阵与 sudo 重执行

平台支持矩阵

"本地跑起来"这件事在各平台并不对称,AGENTS.md 用一张矩阵说明差异(差异的根源是 agent 在每个 OS 上能以何种方式拦截流量):

平台原生二进制(应用跑在宿主机)Keploy 跑在 Docker 内(应用跑在 Docker)
Linux(x86_64、arm64)✅ 支持——eBPF 路径(pkg/agent/hooks/linux/),需要 root✅ 支持
Windows(amd64)✅ 支持——用户态拦截(文档写作路径为pkg/agent/hooks/winshim/),无需驱动、无需 Administrator;纯 Go + 已提交的 shim DLL,构建无额外拉取步骤✅ 支持
Windows(arm64)❌ 落入others兜底桩——Load()/Record()返回 "not supported on non-Linux platforms"✅ 支持
macOS(amd64、arm64)❌ 同为others兜底桩——macOS 没有原生拦截路径✅ 支持(唯一选择)

从当前源码结构看,pkg/agent/hooks/下按 build tag 区分hooks_linux.gohooks_others.go,未支持的架构统一落到 pkg/agent/hooks/others/ 桩实现,与文档中"falls through to theothersstub"的描述一致。

macOS 上的唯一路径是 Docker 模式,AGENTS.md 给出的三步流程:

  1. 构建镜像:sudo docker image build -t ghcr.io/keploy/keploy:v3-dev .
  2. 把应用跑进 Docker(通常经docker compose);
  3. 以该镜像运行 keploy 并挂载到应用容器上——应用若不在 Docker 里,keploy 在 macOS 上无法拦截其流量。这也解释了为什么 CI 里 .github/workflows/prepare_and_run_macos.yml 只调用golang_docker_macos.yml:不存在 macOS 原生等价工作流。

Linux 两种模式任选(CI 两种都跑:golang_linux.yml+golang_docker.yml);Windows amd64 原生无需 sudo,Docker 模式同样可用。

Linux 原生运行示例:

sudo ./keploy record -c "<your app cmd>" sudo ./keploy test -c "<your app cmd>" --delay 10

sudo 自动重执行机制

一个容易踩坑的细节:如果在 Linux 上不带 sudo 传入docker/docker compose作为-c参数,main.go 会在一切初始化之前做"早期检查":utils.ShouldReexecWithSudo()命中后调用utils.ReexecWithSudo(logger),后者用syscall.Execsudo -E替换当前进程(实现见 utils/reexec_linux.go)。在 macOS/Windows 上同名 helper 直接短路返回 false(utils/reexec_darwin.go、utils/reexec_windows.go),因为这两个平台依赖当前 Docker 上下文 / Docker Desktop。

Docker 镜像

sudo docker image build -t ghcr.io/keploy/keploy:v3-dev .

ghcr.io/keploy/keploy:v3-dev是 CI 为 dev 构建产出的镜像 tag,也是 samples 仓库所期望的 tag。

Lint 与提交规范

golangci-lint(配置 schema v2)

配置文件为 .golangci.yml:

  • 启用的 linter:govetstaticcheckerrcheckineffassignunused
  • 格式化工具:gofmtgoimports
  • 从 lint 中排除的路径:生成的 eBPF Go 文件(文档写作时是pkg/agent/hooks/bpf_arm64_bpfel.gobpf_x86_bpfel.go)以及pkg/service/utgen

运行命令:golangci-lint run

这里恰好是文档"相信磁盘"原则的实例:当前树中这两个生成文件实际位于 pkg/agent/hooks/linux/bpf_arm64_bpfel.go 与 pkg/agent/hooks/linux/bpf_x86_bpfel.go,且.golangci.yml的排除列表仍登记着旧路径。阅读者应以磁盘为准理解"生成代码不可手改"这一规则本身。

Commit hygiene

  • .pre-commit-config.yaml 在commit-msg阶段挂接commitizen(Conventional Commits);
  • .cz.toml 把约定固定为cz_conventional_commits,类型使用feat:fix:chore:refactor:test:docs:等;
  • 每个提交必须有正文说明(空一行后写"改了什么、为什么改");
  • 每个提交必须git commit -s签名——它追加Signed-off-by: <user.name> <user.email>trailer,取值来自生效的 git 配置(system →~/.gitconfig.git/config)。文档明确提示不要手工拼 trailer,让 git 从配置读取身份,保证与 author 一致。

涉及 PR/Issue 时还有一条卫生红线:不要把真实 trace、token、内部主机名或生产日志粘进 PR、Issue、测试与提交的 fixture 中;AGENTS.md 指引读者进一步参考keploy-pr-workflowskill(位于.claude/skills/下)与keploy-docsskill 了解 PR 模板、行为变更时需要同步更新的文档位置。

用户可见命令速查

AGENTS.md 提供了一张"用户视角"命令总表,并规定keploy --help是权威信息源——增删改任何命令时,必须在同一提交里更新此表:

命令所在包作用
keploy record -c "<cmd>"pkg/service/record运行应用,把依赖流量捕获进./keploy/test-set-*
keploy test -c "<cmd>"pkg/service/replay重放已录制的调用、mock 依赖,写出./keploy/reports/test-run-*
keploy rerecord -c "<cmd>"pkg/service/orchestrator对新代码重新录制,吸收已接受的变更
keploy normalizepkg/service/tools把新观察到的响应接受进 golden 测试用例
keploy sanitizepkg/service/toolscustom_gitleaks_rules.toml+ 内置规则清洗机密
keploy templatizepkg/service/tools在测试集中把动态值替换为模板
keploy config --generatecli/config.go写出默认keploy.yml
keploy contract ...pkg/service/contractOpenAPI 契约生成/测试
keploy diff <r1> <r2>pkg/service/diff对两次 test run 做 diff
keploy reportpkg/service/report汇总一次历史 test run
keploy export/importcli/export.go、cli/import.go在仓库间搬运 test-set
keploy updatecli/update.go自更新二进制
keploy agentcli/agent.go内部命令——Docker 镜像 entrypoint 使用

文档同时明确标注了一个"看起来像功能但实际不可用"的陷阱:keploy genpkg/service/utgen,LLM 单测生成)的 CLI 注册在文档写作时处于被注释状态——实现存在但命令未接线,不要把它当作用户可见功能去宣传。(注:当前树中cli/下已看不到utgen.go,再次印证以磁盘为准。)

磁盘产物格式:脚本解析报告的"地面真值"

keploy record之后:

keploy/ ├── test-set-0/ │ ├── tests/ │ │ ├── test-1.yaml │ │ └── test-2.yaml │ └── mocks.yaml (或 mocks/ 目录,取决于版本) ├── test-set-1/ │ └── ...

keploy test之后:

keploy/ └── reports/ └── test-run-0/ (最新 run 编号最大) ├── test-set-0-report.yaml (顶层字段: status: PASSED|FAILED) ├── test-set-1-report.yaml └── coverage.yaml (开启覆盖率时才有)

status:行就是脚本 grep 的地面真值。AGENTS.md 指定了标准解析循环的参照实现:.github/workflows/test_workflow_scripts/golang/echo_mysql/golang-linux.sh。

Keploy 仓库特有的编码约定

以下约定全部出自 AGENTS.md 的 "Conventions → Keploy-specific" 一节,每一条都能在源码中找到对应证据:

  • 包文档注释——每个package foo必须以// Package foo ...开头(如 main.go 的// Package main is the entry point...)。
  • 根 context——从 utils/ctx.go 的utils.NewCtx()获取可取消的根 context。它注册了 SIGINT/SIGTERM 信号处理并在收到信号时调用cancel,服务代码中禁止直接使用context.Background()。读源码可以看到它还有两个进阶设计:RegisterPreCancelHook允许子系统在cancel()之前的瞬间同步读取实时状态(例如 agent 在关机瞬间把 syncMock 缓冲状态直写 stderr,因为 zap 的异步日志可能来不及 flush);以及KEPLOY_SIDECAR_DRAIN_SECONDS驱动的 sidecar 优雅排空窗口。
  • 协程生命周期——任何 goroutine 都用errgroup.WithContext(ctx)组织,而不是裸go func()sync.WaitGroup。工作按"每阶段一个 errgroup"拆分(setup / run-app / req),各阶段拥有独立 cancel,使某阶段可以单独拆除而不拖垮其他阶段。AGENTS.md 指认 pkg/service/record/record.go 为规范布局——当前树中这些 errgroup 分别出现在Start流程的errGrprunAppErrGrpsetupErrGrpreqErrGrp(约 L588-L635 一带)。
  • 日志——显式传递*zap.Logger(不用全局变量),经utils/log.New()构建一次;错误上报用utils.LogError(logger, err, "msg", ...fields)替代logger.Error(...)——它会丢弃context.Canceled,避免预期内的关机路径刷爆日志(注意它设置ErrCode,那是独立机制;实现见 utils/utils.go)。文档还要求:出了问题时,日志应同时告诉用户"下一步该做什么"。
  • 退出码——utils.ErrCode是包级int,main.go 将其传给os.Exit。想让进程非零退出就置 1;文档称目前只有pkg/service/replay/replay.go会在测试失败时翻转它。
  • 错误处理——用fmt.Errorf("...: %w", err)包装;应用生命周期失败用models.AppError+models.AppErrorType分类(字符串枚举位于 pkg/models/errors.go);优先errors.Is/errors.As而非字符串匹配;携带诊断载荷的自定义错误(如mockMismatchError)必须实现Unwrap()
  • 配置访问——服务层从 cli/provider/ 装配好的*config.Config读取;禁止在pkg/service/pkg/core/里直接os.Getenv——要新增字段就加进config.Config,在cmdConfigurator/main.go解析并透传。
  • 接口住在消费方——每个pkg/service/<name>/service.go定义本包依赖的小接口(TestDBMockDBTelemetryInstrumentation等),保持 1–10 个方法、按职责塑形;具体实现放在独立包(如 pkg/platform/yaml/testdb/),在 cli/provider/core_service.go 接线。
  • Context 感知 I/O——长流程应使用 pkg/platform/yaml/ 中的ctxReader/ctxWriter,让文件 I/O 也能响应取消。
  • 生成代码——eBPF Go 文件bpf_*_bpfel.go.c源码生成,永不手改,且在.golangci.yml中被排除;探针行为需要变化时应走 eBPF 工具链重新生成。

通用 Go 卫生规范

AGENTS.md 的 "General Go hygiene" 一节列出了整个代码库遵循的通用规则:

  • 接受接口,返回结构体;接口定义在"使用点"而非"实现点"。单一实现且单一消费方时,通常还不需要接口。
  • context.Context是导出方法的第一个参数(接收者之后、一切之前);context 不得存入 struct。
  • 不要把状态塞进context.WithValue——它只承载请求级值(trace ID、认证信息),依赖注入走结构体字段或函数参数。本仓库唯一被容忍的例外是models.ErrGroupKey(携带父 errgroup)。
  • 只在边界 panic——库与服务代码返回错误;recover只出现在顶层 goroutine 入口与main(见utils.Recover,main.go 即以defer utils.Recover(logger)收口)。不要用 panic 表达预期失败。
  • testify表格驱动测试——fail-fast 用require,需要继续的用assert。文档坦承:本仓库单测覆盖较稀疏,协议级行为主要靠keploy-e2e-testskill 描述的 e2e 验证,因此纯逻辑写表格测试,跨边界行为交给 e2e。
  • 每个导出符号都要有文档注释<Name> does X.句式——godoc 是公共契约。
  • 函数保持短小、意图显式——嵌套超过约 3 层或逻辑超过约 50 行就应先拆分;函数命名要值回票价(parseRecordFrame胜过上方注释块加doStep)。
  • 最小化导出面——只有真正需要离开包才大写开头;收缩公共 API 是最便宜的重构。

文档最后留了一句缓冲:"如果你对某条约定没有十足把握,可以忽略它"——约定服务于正确与可维护,而不是教条。

CI 全景:入口、矩阵与兼容性闸门

入口工作流

  • .github/workflows/prepare_and_run.yml —— Linux 主 CI,在 PR 到mainmain推送时运行,其余工作流都是它的下游;
  • .github/workflows/prepare_and_run_macos.yml —— macOS(self-hosted);
  • .github/workflows/prepare_and_run_windows.yml —— Windows 等价物;
  • .github/workflows/prepare_and_run_integrations.yml —— 私有 parser 专用矩阵子集(面向 integrations 仓库);
  • .github/workflows/manual-release.yml —— 仅workflow_dispatch,触发企业版流水线。

prepare_and_run.yml做什么

  1. 预先构建三个二进制,供矩阵任务自由组合:
    • build-no-racego build -tags=viper_bind_struct
    • buildgo build -race -tags=viper_bind_struct(CGO 开)
    • latest:下载最近的 GitHub release
    • 三者各自以同名 artifact 上传。
  2. 构建 Docker 镜像并推送到ttl.sh,带每次运行唯一的 tag。
  3. workflow_call扇出到各语言工作流:golang_linux.yml(samples-go,原生 Linux)、golang_docker.yml(Docker 镜像路径)、golang_wsl.yml(WSL)、python_linux.yml/python_docker.yml(samples-python)、node_linux.yml/node_docker.yml(samples-typescript)、java_linux.yml(samples-java)、grpc_linux.yml(gRPC 专用 go 矩阵)、schema_match_linux.yml(schema-match 矩阵,python 侧)、以及专用的fuzzer_linux.ymlnode_mapping.yml
  4. 收敛到单一gate任务——它唯一是必需的 status check;单独重跑gate没有意义,它只是复检上游结果。

矩阵结构:保证 mock 格式前后兼容的关键

每个语言工作流都遵循同一矩阵模式:

matrix: app: - name: <display-name> path: <directory in samples-<lang> repo> script_dir: <directory in .github/workflows/test_workflow_scripts/<lang>/> config: - job: record_latest_replay_build # 用发布版录制,用本 PR 构建重放 record_src: latest replay_src: build - job: record_build_replay_latest # 用本 PR 构建录制,用发布版重放 record_src: build replay_src: latest - job: record_build_replay_build # 两端都是本 PR 构建——验证同版本行为 record_src: build replay_src: build

这三个config条目是 CI 保证mock 格式向后/向前兼容的机制:任何新变更必须能与上一个发布版二进制双向互操作。如果你的改动会破坏任一方向,就必须加门控(capability 检测、版本检查或 feature flag)——文档给出具体范例risk_profile/golang-linux.sh,其中按case "${REPLAY_BIN:-}" in */build/keploy) ...分支处理。

每个矩阵任务以这样的脚本步骤收尾:

cd samples-<lang>/${{ matrix.app.path }} source $GITHUB_WORKSPACE/.github/workflows/test_workflow_scripts/<lang>/${{ matrix.app.script_dir }}/<lang>-linux.sh

脚本通过RECORD_BINREPLAY_BIN环境变量接收二进制路径,由./.github/actions/download-binary复合 action 设置。

样例测试脚本的标准结构

所有样例脚本遵循同样的 11 步骨架:

  1. source .../test_workflow_scripts/test-iid.sh——写一个假~/.keploy/installation-id.yaml,避免遥测初始化时交互提示;
  2. 清理上一轮遗留的keploy/keploy.yml
  3. 执行$RECORD_BIN config --generate,可选地用sedkeploy.yml注入噪声规则(例如global: {"body": {"updated_at":[]}});
  4. 拉起依赖容器(MySQL、Postgres、Mongo、Redis)并等待就绪;
  5. 构建样例应用(go buildmvn packagenpm cipip install……);
  6. 定义send_request()——等应用健康检查、灌流量、sleep、按 PID 杀掉 keploy(pgrep keploy);
  7. record在后台跑一次或两次并tee日志,随后 grep"ERROR""WARNING: DATA RACE"(两者都是致命的);
  8. 可选:replay 前停掉 DB 容器(强制 Keploy 走 mock——捕获"mock 漏配"回归);
  9. 执行replay"$REPLAY_BIN" test -c "./app" --delay N --generateGithubActions=false 2>&1 | tee test_logs.txt
  10. 遍历./keploy/reports/test-run-*/test-set-*-report.yamlls -1dt ... | head -n1取最新),grep 每个报告的status:,出现非PASSED即失败;
  11. 成功退出 0,任一失败退出 1。

CI 拉取的样例仓库

CI 工作流样例仓库样例位置
golang_linux.ymlgolang_docker.ymlgolang_wsl.ymlgrpc_linux.ymlschema_match_linux.yml(go 侧)keploy/samples-gosamples-go/<path>
python_linux.ymlpython_docker.ymlschema_match_linux.yml(python 侧)keploy/samples-pythonsamples-python/<path>
node_linux.ymlnode_docker.ymlnode_mapping.ymlkeploy/samples-typescriptsamples-typescript/<path>
java_linux.ymlkeploy/samples-javasamples-java/<path>

常见改动的入口目录速查

AGENTS.md 的收尾是一张"改 X 从哪看起"的索引表:

如果要改……从这里开始
某协议的录制/重放行为pkg/core/proxy/ + pkg/models/ 下对应协议包
mock 匹配逻辑pkg/matcher/ + pkg/service/replay/
磁盘 YAML 格式pkg/platform/yaml/ + pkg/models/mock.go、pkg/models/testcase.go
CLI flagcli/ 下对应命令文件 + cli/provider/
配置默认值config/default.go + config/config.go
测试报告pkg/service/report/
覆盖率pkg/platform/coverage/
eBPF 探针行为pkg/agent/hooks/(C 源码——不要编辑生成的 Go)
给 CI 加样例样例仓库(samples-go等)+.github/workflows/test_workflow_scripts/<lang>/<script_dir>/+ 对应 workflow 里加一条矩阵项

文档最后的实践建议:为行为变更补 e2e 覆盖时,优先扩展现有样例及其脚本,而不是新建;完整决策树见keploy-e2e-testskill。

小结

AGENTS.md 的价值在于把"在这个仓库里正确做事"压缩成了一份可执行清单:构建必须带viper_bind_struct、macOS 只能走 Docker、status: PASSED是报告解析的地面真值、errgroup 分阶段管理协程、CI 三向矩阵锁住 mock 格式兼容性。它同时以"相信磁盘"的元规则提醒读者——生成代码路径、lint 排除项这类细节会随仓库演进漂移,动手前以实际文件为准。对贡献者与 AI Agent 而言,这份文档加上文中引用的源码路径,就构成了从构建到提交、从约定到 CI 的完整导航图。

【免费下载链接】keployOpen-source platform for creating safe, isolated production sandboxes for API, integration, and E2E testing.项目地址: https://gitcode.com/GitHub_Trending/ke/keploy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 11:03:28

3C精密零件厚度检测:激光位移传感器选型避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:59:52

基于Q-Learning的无人机三维避障算法实践

1. 项目概述&#xff1a;当无人机遇上强化学习去年调试一架四旋翼无人机时&#xff0c;我亲眼目睹它径直撞向一棵突然出现的行道树——传统基于规则的控制算法在动态环境中显得如此笨拙。这次经历促使我开始探索基于Q-Learning的自主避障方案。与静态路径规划不同&#xff0c;动…

作者头像 李华
网站建设 2026/9/13 10:59:38

PostgreSQL与MySQL选型:设计哲学、性能分水岭与迁移实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:59:33

论文写作效率提升:模块化写作与工具链配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:59:07

YOLO与VOC数据集格式一键转换工具实现

1. 项目背景与核心需求在目标检测领域&#xff0c;YOLO和VOC是两种最常用的数据集格式。YOLO格式以简洁的文本标注著称&#xff0c;而VOC格式则采用结构化的XML文件存储更丰富的元信息。实际项目中经常遇到这样的需求&#xff1a;当我们获得一个YOLO格式标注的数据集&#xff0…

作者头像 李华