首页
/ frp 开发工作流详解:构建、测试、代码质量与发布流程(基于 AGENTS.md)

frp 开发工作流详解:构建、测试、代码质量与发布流程(基于 AGENTS.md)

2026-09-03 15:19:06作者:戚魁泉Nursing

frp(fast reverse proxy)仓库在根目录维护了一份面向开发者与 AI Agent 的开发指引 AGENTS.md,系统性地约定了构建、测试、代码质量检查和资产打包等日常开发命令,并指向 doc/agents/ 下的运行手册。本文以该文档为骨架,结合 Makefilehack/run-e2e.sh.golangci.yml 与 e2e 测试框架源码,把每条命令背后的实际行为、参数与适用场景讲清楚,帮助你在本地完整复现 frp 的“构建 — 单测 — e2e — 静态检查 — 发布”全流程。

一、AGENTS.md 文档结构总览

AGENTS.md 是仓库为开发者与 Agent 提供的“操作入口”,内容分为四部分:

章节 核心内容
Development Commands 构建(Build)、测试(Testing)、代码质量(Code Quality)、Web 资产(Assets)、清理(Cleanup)五组 make 命令
Testing e2e 测试的技术栈(Ginkgo/Gomega)、mock 服务器位置、运行入口
Agent Runbooks 指向 doc/agents/ 目录下的运维流程文档,当前包含 doc/agents/release.md(发布流程)

所有命令统一通过根目录 Makefile 驱动,因此理解 Makefile 中各 target 的实现是掌握整个开发工作流的关键。

二、构建系统:make build / make frps / make frpc / make all

AGENTS.md 定义了四个构建目标:

  • make build — 同时构建 frps 和 frpc 两个二进制;
  • make frps — 只构建服务端二进制;
  • make frpc — 只构建客户端二进制;
  • make all — 带格式化的全量构建(等价于 env fmt web build)。

对照 Makefile 源码,frpsfrpc target 的实际执行语句是:

frps:
	env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -tags "frps$(NOWEB_TAG)" -o bin/frps ./cmd/frps

frpc:
	env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -tags "frpc$(NOWEB_TAG)" -o bin/frpc ./cmd/frpc

其中有几个值得注意的细节:

  1. 纯静态编译CGO_ENABLED=0 保证产物不依赖系统 C 库,-trimpath 去除本地路径信息,LDFLAGS := -s -w 则去掉符号表和 DWARF 调试信息以减小体积——这三者组合是典型的跨平台发布编译配置。
  2. frps / frpc 构建标签:两个二进制各自带独立 build tag(-tags "frps" / -tags "frpc"),即 cmd/frpscmd/frpc 两套入口(见 cmd/frps/main.gocmd/frpc/main.go)按标签条件编译,避免把对方的代码带进二进制。
  3. noweb 标签的自动协商:Makefile 第 4 行的 NOWEB_TAG 会检测 web/frps/distweb/frpc/dist 两个目录是否存在,任一缺失就追加 ,noweb 标签。配合 web/frps/embed.goweb/frps/embed_stub.go 这类成对的构建约束文件:当先执行 make web 产出 dist 后,Web 控制台会被真正嵌入二进制;未构建前端时,嵌入逻辑退化为空实现(stub),Go 代码依然可编译。

因此推荐的标准构建顺序是 make all 或先 make webmake buildmake all 的依赖链为 env fmt web build,即打印 Go 版本、格式化代码、构建 Web 资产、再构建二进制,一步到位。

Web 资产目标本身很简单,web/frps/Makefile 与 web/frpc 的 Makefile 一致:

install:
	@cd .. && npm install

build: install
	@npm run build

make frps-web / make frpc-web 实际是进入 web/frpsweb/frpc 子目录执行 npm 安装与 Vite 构建(两个前端工程均为 Vue + Vite + npm workspace 组织,见 web/package.json)。

三、单元测试:make test

make test 依赖 gotest target,其实现为对五个顶层包分组执行带覆盖率的测试:

gotest:
	go test -tags "$(NOWEB_TAG)" -v --cover ./assets/...
	go test -tags "$(NOWEB_TAG)" -v --cover ./cmd/...
	go test -tags "$(NOWEB_TAG)" -v --cover ./client/...
	go test -tags "$(NOWEB_TAG)" -v --cover ./server/...
	go test -tags "$(NOWEB_TAG)" -v --cover ./pkg/...

可以看到,单测覆盖 assets/cmd/client/server/pkg/ 五大模块——分别对应前端资产、命令行入口、客户端(代理、visitor、控制连接)、服务端(代理、组、注册表)和公共库(消息协议、配置解析、传输、限流等)。-tags "$(NOWEB_TAG)" 与构建保持同一套条件编译约定,保证无论 Web dist 是否存在,测试编译都能通过。

四、端到端测试:Ginkgo/Gomega 与 mock 服务器

AGENTS.md 在 Testing 章节明确了两点事实:

  • e2e 测试基于 Ginkgo/Gomega 框架;
  • mock 服务器位于 test/e2e/mock/,运行入口为 make e2emake alltest

结合仓库源码可以进一步展开。

4.1 入口脚本 hack/run-e2e.sh

make e2e 实际执行 hack/run-e2e.shmake e2e-trace 则是带 DEBUG=true LOG_LEVEL=trace 环境的同一脚本。脚本的关键逻辑:

# Check if ginkgo is available
if ! command -v ginkgo >/dev/null 2>&1; then
    go install github.com/onsi/ginkgo/v2/ginkgo@v2.23.4
fi
...
frpcPath=${ROOT}/bin/frpc        # 可被 FRPC_PATH 覆盖
frpsPath=${ROOT}/bin/frps        # 可被 FRPS_PATH 覆盖
concurrency="16"                 # 可被 CONCURRENCY 覆盖

ginkgo -nodes=${concurrency} --poll-progress-after=60s ${ROOT}/test/e2e \
    -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug}

从脚本结构看,有三个实用要点:

  1. ginkgo 自动安装:环境里没有 ginkgo 时会以固定版本 v2.23.4 自动 go install,降低环境准备成本;
  2. 二进制路径可注入FRPC_PATH / FRPS_PATH 环境变量允许指定非本仓库构建的二进制。这正是版本兼容测试的机制——Makefile 中的 e2e-compatibility-last-frpc / e2e-compatibility-last-frps target 就是先用 hack/download.sh 拉取上一版本发布二进制到 lastversion/,再以 FRPC_PATH=... FRPS_PATH=... 方式跑 e2e,验证新旧版本混跑;
  3. 默认 16 并行-nodes=16 对应 Ginkgo 的并行模式,可用 CONCURRENCY 调整。

4.2 测试框架与套件组织

测试入口 test/e2e/e2e.go 中,RunE2ETests 通过 ginkgo.RunSpecs 启动名为 "frp e2e suite" 的套件,并用 ginkgo.SynchronizedBeforeSuite / SynchronizedAfterSuite 管理并行节点间的初始化与清理——这是 Ginkgo 并行测试的标准做法,保证全局 setup 只执行一次。套件本身随机化所有 spec(RandomizeAllSpecs = true),避免用例间顺序依赖。

按目录组织,e2e 分为几层(见 test/e2e/suites.go):

  • test/e2e/v1/:v1 协议的完整功能测试,按 basic/(TCP、HTTP、tcpmux、xtcp、token、OIDC 等基础场景)、features/(带宽限制、心跳、组、真实 IP、store、SSH 隧道等特性)、plugin/(客户端/服务端插件)分组;
  • test/e2e/legacy/:面向旧版本兼容场景的测试集;
  • test/e2e/compatibility/:跨版本兼容套件;
  • test/e2e/framework/:框架层,封装了进程管理、期望断言、mock 服务、请求与 RPC 等测试基建。

4.3 mock 服务器

AGENTS.md 提到的 /test/e2e/mock/ 目录在仓库中位于 test/e2e/mock/,其 server/ 子目录提供三类测试用服务:

  • httpserver/ — 模拟 HTTP 流量目标;
  • streamserver/ — 模拟 TCP 流式服务;
  • oidcserver/ — 模拟 OIDC 身份认证服务,供 pkg/auth/oidc.go 相关的认证测试使用。

它们共同实现一个极简的 Server 接口(test/e2e/mock/server/interface.go):

type Server interface {
	Run() error
	Close() error
	BindAddr() string
	BindPort() int
}

框架层启动这些 mock 后,通过 BindPort() 拿到随机端口动态生成 frpc/frps 配置,避免测试间的端口冲突。

4.4 全部测试:make alltest

make alltest 是发布前的完整质量门禁,Makefile 中定义为:

alltest: vet gotest e2e

即依次执行 go vet、分组单测(含覆盖率)、e2e 套件三步,与 AGENTS.md 中 “Run all tests including vet, unit tests, and e2e” 的描述完全对应。

五、代码质量:格式化、import 组织、vet 与 golangci-lint

AGENTS.md 的 Code Quality 章节列出的命令与 Makefile 实现一一对应:

命令 Makefile 实现 说明
make fmt go fmt ./... 标准 Go 格式化
make fmt-more gofumpt -l -w . gofumpt 更严格格式化
make gci gci write -s standard -s default -s "prefix(github.com/fatedier/frp/)" ./ import 分组排序:标准库、第三方、frp 自身包
make vet go vet -tags "$(NOWEB_TAG)" ./... 官方静态检查
golangci-lint run 综合 lint,配置见 .golangci.yml

其中 gci 的三段式分组规则(standard / default / prefix(github.com/fatedier/frp/))与 .golangci.yml 中 formatters 部分的 gci 配置完全一致,保证手工排序与 CI 检查口径统一。

.golangci.yml 采用的是 golangci-lint v2 配置格式,启用的 linter 包括 errcheckgocriticgosecgovetineffassignlll(行宽 160)、misspellpreallocrevivestaticcheckunparamunused 等十余项,formatters 启用 gcigofumptgoimports;同时通过 exclusions 排除了生成文件(*.pb.go*.gen.go)、vendor/bin/node_modules 等路径,并对测试文件放宽了 errcheck 要求。CI 侧由 .github/workflows/golangci-lint.yml 工作流执行同一配置。

六、清理:make clean

make clean 用于移除构建产物与缓存目录,对应实现:

clean:
	rm -f ./bin/frpc
	rm -f ./bin/frps
	rm -rf ./lastversion
	rm -rf ./.cache
	rm -rf ./.compat

除常规二进制外,还会清理版本兼容测试的 lastversion/(上一版本二进制)以及 .cache/.compat/ 缓存目录——后者用于缓存 e2e 兼容性测试下载的发布二进制(缓存路径为 .cache/e2e-compat/<version>/<os>_<arch>/,见 doc/agents/release.md)。

七、Agent Runbooks:发布流程与关键文件

AGENTS.md 最后一节指出运维流程文档位于 doc/agents/ 目录,目前包含 doc/agents/release.md。该 runbook 完整描述了 frp 的发布流程,核心要点:

  1. 更新 Release Notes:编辑根目录 Release.md,按 Features / Improvements / Fixes 三段组织,该文件会被 GoReleaser 用作 GitHub Release 的正文;
  2. 提升版本号:修改 pkg/util/version/version.go 中的版本字符串(当前仓库值为 0.71.0),提交并推送到 dev 分支;
  3. 预发布验证:本地跑 make e2e;若改动触及登录、控制连接、工作连接、visitor、传输或 wire 协议等兼容敏感区域,还需执行 make e2e-compatibilitymake e2e-compatibility-floor。这两个 Makefile target 会解析近期稳定版作为基线、下载(或复用缓存的)历史版本二进制,与当前构建产物做混跑测试,基线数量由 FRP_COMPAT_BASELINE_COUNT(默认 8)控制,兼容性下限版本由 FRP_COMPAT_FLOOR_VERSION(当前 0.61.0)控制,也可用 FRP_COMPAT_BASELINE_VERSIONS 指定显式基线矩阵以保证结果可复现;
  4. dev 合入 master:以 merge commit(而非 squash)方式合并;
  5. 打 tag 并触发 GoReleasergit tag -a vX.Y.Z 后手动触发 goreleaser 工作流,其执行 package.sh 完成跨平台交叉编译与打包(跨平台矩阵定义于 Makefile.cross-compiles),最终创建包含全部平台的 GitHub Release;
  6. 版本号策略:Minor 发布为 v0.X.0,Patch 发布为 v0.X.Y

runbook 中还以表格形式列出了发布关键文件:

文件 用途
pkg/util/version/version.go 版本字符串
Release.md 发布说明(GoReleaser 读取)
package.sh 交叉编译与打包脚本
.github/workflows/goreleaser.yml 手动触发的 GoReleaser 工作流

八、本地快速上手清单

按 AGENTS.md 与 Makefile 的实际行为,一个从零开始的本地开发验证顺序可以是:

# 1. 构建(自动完成格式化、Web 资产、双二进制构建)
make all

# 2. 仅跑单元测试(含覆盖率)
make test

# 3. 代码质量检查
make vet
golangci-lint run

# 4. 端到端测试(依赖 bin/ 下已构建的二进制)
make e2e

# 5. 完整质量门禁
make alltest

# 6. 清理
make clean

需要注意的前提:make e2e 依赖先构建出 bin/frpcbin/frps(Makefile 的 e2e-compatibility* target 已自动声明 build 依赖,但 e2e 本身不会触发构建,脚本只是引用 ./bin/ 路径);make web 需要 Node.js/npm 环境;ginkgo 缺失时脚本会自动安装固定版本。

九、小结

AGENTS.md 虽然篇幅不长,但给出了 frp 仓库完整的开发命令面:构建(make build/frps/frpc/all)、测试(make testmake e2emake alltest)、代码质量(make fmt/fmt-more/gci/vetgolangci-lint)、Web 资产(make web)与清理(make clean),并通过 doc/agents/ 目录挂载了 发布流程 runbook。结合 Makefilehack/run-e2e.sh.golangci.ymltest/e2e/ 测试框架的实现细节可以看出,这套工作流的设计特点是:构建标签统一(frps/frpc/noweb)、测试分层(vet → 单测 → e2e → 跨版本兼容)、静态检查口径与 CI 完全一致。掌握这些命令及其底层机制,即可在本地完整复现 frp 的 CI 质量门禁与发布验证流程。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384