# Makefile for Flyto Agent core/ — godoc 发布 + 合规 lint + L407 消费层文档自动化. # # 本 Makefile 的开发工具 (staticcheck / pkgsite / swag / protoc-gen-doc) 通过 # `go run @version` 或 `go install @version` 从 module cache ad-hoc # 执行, 不污染 go.mod, 不违反 CONTRIBUTING.md 第八条 (零外部依赖原则针对 # 编译依赖, 不针对开发工具). # # 版本钉在具体 tag, 保证所有开发者/未来 CI 行为一致, 避免 upstream 规则静默 drift. STATICCHECK_VERSION := v0.7.0 PKGSITE_VERSION := latest SWAG_VERSION := v1.16.6 PROTOC_GEN_DOC_VERSION := v1.5.1 # Repo root, resolved once. Lets docs-* targets cd into platform/common from # anywhere (so `make -C core docs-all` works the same as `cd core && make docs-all`). # # 仓根, 一次解析. 让 docs-* target 从任意目录都能 cd 到 platform/common # (`make -C core docs-all` 与 `cd core && make docs-all` 行为一致). ROOT := $(shell git rev-parse --show-toplevel) # Add GOPATH/bin to PATH so docs-swag / docs-grpc find swag and # protoc-gen-doc that docs-install puts there. `go env GOPATH` is # evaluated at parse time once. Without this, Make's default child # shell PATH excludes GOPATH/bin even if the developer's interactive # shell exports it. # # 把 GOPATH/bin 加进 PATH, 让 docs-swag / docs-grpc 找到 docs-install # 装的 swag 和 protoc-gen-doc. `go env GOPATH` parse 时一次性求值. # 否则 Make 默认子 shell 的 PATH 不含 GOPATH/bin, 即使开发者交互 shell # 已 export. export PATH := $(shell go env GOPATH)/bin:$(PATH) .PHONY: help docs-lint docs-serve docs-install docs-swag docs-grpc docs-consumers docs-all dead-field-scan dead-field-scan-ratchet help: @echo "Flyto Agent core/ 开发目标 (make ):" @echo "" @echo " docs-lint 检查 godoc 合规性 (ST1000/ST1020/ST1021)" @echo " 保证 package/exported 方法文档格式正确, 防注释腐化" @echo "" @echo " docs-serve 本地启动 pkgsite HTTP 服务器 (bind localhost:8080)" @echo " 浏览器打开 http://localhost:8080/git.flytoex.net/yuanwei/flyto-agent 查看 HTML 文档" @echo " module 路径走自建 git, pkg.go.dev 不收, 走本地 pkgsite" @echo "" @echo " docs-swag 从 platform/common server.go 注解产 docs/swagger.{json,yaml,docs.go}" @echo " L407 三件套之一; CI 跑 git diff --exit-code 卡 drift" @echo "" @echo " docs-grpc 从 .proto 文件产 platform/common/docs/grpc-api.md" @echo " L407 三件套之一; HealthService + SafetyChainService 字段表" @echo "" @echo " docs-consumers 验证 docs/CONSUMERS.md 存在 (顶层手写 wrapper, 端口/env/flag/auth)" @echo "" @echo " docs-all 一键 docs-swag + docs-grpc + docs-consumers, 用于 CI drift 检查前置" @echo "" @echo " docs-install 固定版本安装 staticcheck + pkgsite + swag + protoc-gen-doc 到 GOPATH/bin" @echo " docs-lint/serve 用 go run 自举, docs-swag/grpc 必须先 install" @echo "" @echo " dead-field-scan 扫 ./... 下 exported struct 的死 exported field" @echo " POC 模式: 只报告, 不 fail build" @echo "" @echo " dead-field-scan-ratchet 对比当前扫描 vs scan-baseline.json" @echo " baseline 只能减不能增. 新增 dead field → exit 1" @echo " CI 挂 .gitea/workflows/release.yml build-push 前置" # go run 自举版本 lint. 开发者零安装, staticcheck 二进制留 module cache. # 钉 @$(STATICCHECK_VERSION) 是关键: 不钉会因 upstream 升级导致本地过/CI 不过. docs-lint: go run honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION) \ -checks ST1000,ST1020,ST1021 ./pkg/... # 本地 HTML 文档站点. pkgsite 只 bind localhost, 不对外暴露. docs-serve: @echo "godoc HTML: http://localhost:8080/git.flytoex.net/yuanwei/flyto-agent (Ctrl+C 停止)" go run golang.org/x/pkgsite/cmd/pkgsite@$(PKGSITE_VERSION) -http=:8080 . # 可选: 固定版本安装到 GOPATH/bin, 避免每次 go run 冷启. 多数人不需要. # swag / protoc-gen-doc 不支持 go run 直接调 (CLI 有 plugin discovery + TLS init # 副作用), 必须 install. 它们也在 release.yml docs drift step 跑前先 install. # # Optional: pin versions install to GOPATH/bin to avoid each-run cold start. # Most developers do not need this. swag and protoc-gen-doc cannot be run # via `go run` directly (CLI has plugin discovery and TLS init side effects), # so they must be installed; they are also installed in release.yml's docs # drift step before running. docs-install: go install honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION) go install golang.org/x/pkgsite/cmd/pkgsite@$(PKGSITE_VERSION) go install github.com/swaggo/swag/cmd/swag@$(SWAG_VERSION) go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@$(PROTOC_GEN_DOC_VERSION) # Generate Swagger 2.0 spec from server.go @-block annotations. The seed # file is internal/server/swag.go (service-level @title / @host / @BasePath). # swag scans the directory recursively for handler-level @Summary / @Param / # @Success blocks. Output is platform/common/docs/{swagger.json,swagger.yaml, # docs.go}; docs.go is a Go embed file imported via side effect by # internal/server/server.go so cmd/common --swagger can serve it without # reading the filesystem. # # 从 server.go @-block 注解产 Swagger 2.0 spec. seed file 是 # internal/server/swag.go (service-level @title / @host / @BasePath). swag # 递归扫目录收 handler-level @Summary / @Param / @Success. 输出是 # platform/common/docs/{swagger.json,swagger.yaml,docs.go}; docs.go 是 Go # embed file, 经 internal/server/server.go side-effect import 嵌入, # cmd/common --swagger 不读文件系统就能 serve. docs-swag: @command -v swag >/dev/null 2>&1 || { echo "swag not installed; run 'make docs-install'"; exit 1; } cd $(ROOT)/platform/common && swag init -g swag.go -d ./internal/server -o ./docs --parseDependency --parseInternal # Generate Markdown gRPC API doc from .proto files (HealthService + # SafetyChainService). protoc + protoc-gen-go are already used to regen # *.pb.go; protoc-gen-doc here only emits a markdown table-of-fields # alongside the existing pb gen, no source-of-truth duplication. # Output is platform/common/docs/grpc-api.md. # # 从 .proto 产 gRPC API markdown 文档. protoc + protoc-gen-go 已用于 regen # *.pb.go; protoc-gen-doc 只产 markdown 字段表, 不重复 source-of-truth. # 输出 platform/common/docs/grpc-api.md. docs-grpc: @command -v protoc-gen-doc >/dev/null 2>&1 || { echo "protoc-gen-doc not installed; run 'make docs-install'"; exit 1; } @command -v protoc >/dev/null 2>&1 || { echo "protoc not installed (system pkg, e.g. apt install protobuf-compiler)"; exit 1; } cd $(ROOT)/platform/common && protoc \ --doc_out=./docs --doc_opt=markdown,grpc-api.md \ -I ./internal/api/grpc/proto \ ./internal/api/grpc/proto/health.proto \ ./internal/api/grpc/proto/safetychain.proto # CONSUMERS.md is hand-written (~80 lines: ports / env / flags / auth flow). # This target only verifies it exists; drift check at content level would # require checksumming and the human-authored nature makes that low-value. # The swag/grpc drift checks catch divergence at the spec level; CONSUMERS # stays a thin wrapper that links to the auto-gen artifacts. # # CONSUMERS.md 是人工写 (~80 行: 端口/env/flag/auth 流程). 本 target 只验 # 存在; 内容级 drift 校验需 checksum, 人工内容性质让其价值低. swag/grpc # 的 spec-level drift 已捕获偏差; CONSUMERS 保持 thin wrapper 链 auto 产物. docs-consumers: @test -f $(ROOT)/docs/CONSUMERS.md && echo "✓ docs/CONSUMERS.md present" || { echo "✗ docs/CONSUMERS.md missing"; exit 1; } # 一键产全部 (CI 用): docs-swag + docs-grpc + docs-consumers. # release.yml docs drift step 先 docs-install 然后跑 docs-all + git diff --exit-code. # # One-shot for CI: docs-swag + docs-grpc + docs-consumers. release.yml docs # drift step runs docs-install then docs-all then git diff --exit-code. docs-all: docs-swag docs-grpc docs-consumers # Dead exported field scanner. Runs from tools/dead-field-scan as an # isolated Go module so its golang.org/x/tools dep never leaks into # core/go.mod. GOWORK=off prevents the parent go.work from re-attaching # the scanner to core/ during build. # # POC mode: always prints the findings list, always exits 0. The CI gate # in .gitea/workflows/release.yml stays un-wired until a whitelist / # SDK-input-struct annotation pass is agreed. Rationale: raw scan output # mixes true dead fields (e.g. engine.Config.Verbose) with false positives # (Event structs read by external consumers, JSON-serialized fields read # via reflection), so gating on zero findings would block every release. # # dead-field-scan: 扫 exported struct 上被声明但扫描模块内从未被读的 # exported field. 独立 go.mod 避免依赖污染 core/go.mod; GOWORK=off 阻止 # 父 go.work 把工具重新挂回 core/ module. # # POC 模式: 总是打印发现清单, 总是 exit 0. .gitea/workflows/release.yml # 里的 CI gate 等 whitelist / SDK-input-struct 注解策略商定后再 wire. # 理由: raw 清单混合真死字段 (e.g. engine.Config.Verbose) 和假阳性 # (Event struct 被外部 consumer 读, JSON 序列化字段通过反射读), 直接卡 # 0 发现会挡每一次 release. dead-field-scan: cd tools/dead-field-scan && GOWORK=off go run . -root=../.. # Ratchet mode: fail if new dead fields introduced since scan-baseline.json. # See tools/dead-field-scan/ratchet.sh for the full rationale (ratchet vs # whitelist vs gate-on-zero). # # Ratchet 模式: 自 scan-baseline.json 以来有新增 dead field 就 fail. # 设计理由见 tools/dead-field-scan/ratchet.sh (ratchet vs whitelist vs # 卡 0 三者的权衡). dead-field-scan-ratchet: bash tools/dead-field-scan/ratchet.sh