31 KiB
31 KiB
工作区与工程脚本代码审计报告
| 项 | 内容 |
|---|---|
| 审计对象 | 工作区定义 go.work、go.work.sum;工程脚本 scripts/**(verify-workspace.{sh,ps1}、build-all-linux.sh、generate-protobuf.sh、update-all.sh、api-docgen/**) |
| 所属域 | 工程与构建基础设施(非业务代码) |
| 审计日期 | 2026-09-22 |
| 代码规模 | Shell 4 个 / 251 行;PowerShell 2 个 / 44 行(verify-workspace.ps1 34 + api-docgen/generate.ps1 10);api-docgen/main.go 300 行;go.work 26 行;go.work.sum 1205 行;全仓库 go.sum 21 个 |
| 工作区构成 | go.work 的 use 20 项 = module/ 下 18 个模块 + pkgs/all + pkgs/ecmall(实测 go list -m 恰好返回这 20 个);scripts/api-docgen 不在工作区内 |
| 结论摘要 | 脚本整体遵循"先发现、再执行"的写法并普遍使用 set -Eeuo pipefail,update-all.sh 还做了模块空集合校验;但存在三类结构性问题:① go.work 与全部 20 个 go.mod 把 SDK 替换到仓库外的相对路径 ../../bsm-sdk/core,两个仓库必须以固定名字并列检出,否则整个工作区无法解析;② scripts/api-docgen 是工作区外 module,当前检出下直接构建失败(updates to go.mod needed),依赖版本也落后于工作区(protobuf 1.36.11 vs 1.36.12 等),而 update-all.sh/verify-workspace.* 都不覆盖它;③ 校验脚本的 gofmt 门禁只覆盖 pkgs module(漏 scripts/)、且对"模块列表为空"不报错,构建脚本只覆盖 module/(聚合入口既无构建也无生产配置)。此外有若干 GNU 专属语法与 README 的 Linux/macOS 双平台声明不符,以及两处无确认的破坏性删除/批量改写。 |
1. 定位与职责
本对象是整个仓库的工程基础设施层,不含业务逻辑:
go.work/go.work.sum:把 18 个业务模块与 2 个聚合入口组织成一个 Go workspace,并把私有 SDKgit.apinb.com/bsm-sdk/core指向工作区外的本地目录。scripts/verify-workspace.{sh,ps1}:提交前的格式化与测试门禁(gofmt → 逐模块go vet ./...→go test ./...)。scripts/build-all-linux.sh:交叉编译 Linux/amd64 静态二进制并收集产物与生产配置。scripts/generate-protobuf.sh:按模块重跑 protoc 生成pb/代码(go / go-grpc / grpc-gateway / slc)。scripts/update-all.sh:全工作区依赖升级 +go mod tidy+go work sync。scripts/api-docgen:独立的文档生成 module,从 protobuf descriptor 渲染wiki/api/NN-*.md。
2. 代码结构与关键文件
| 路径 | 行数 | 职责与关键点 |
|---|---|---|
go.work |
26 | go 1.27.1;use 20 个模块(go.work:3-24);replace git.apinb.com/bsm-sdk/core => ../../bsm-sdk/core(go.work:26);无 toolchain 指令 |
go.work.sum |
1205 | 工作区级校验和;bsm-sdk/core 只有 v0.2.0/go.mod 一项(go.work.sum:89),而 20 个 go.mod 全部 require v0.2.1 |
scripts/verify-workspace.sh |
28 | bash:gofmt 检查 → mapfile 取模块 → 逐模块 vet/test;SKIP_VET=1 可跳过 vet |
scripts/verify-workspace.ps1 |
34 | PowerShell 等价实现(-SkipVet),校验 vet/test 的 $LASTEXITCODE |
scripts/build-all-linux.sh |
105 | 校验 go/go.work → 导出 GOOS/GOARCH/CGO_ENABLED → 收集 module/ 下所有 go.mod → 逐个 go build -trimpath → 收集 {basename}_prod.yaml |
scripts/generate-protobuf.sh |
61 | 工具预检 → 逐模块清空 pb/ 旧生成物 → protoc 生成 → 可选 protoc-gen-slc |
scripts/update-all.sh |
57 | 模块列表空校验 → 逐模块 go get -u ./...(失败则用 genproto@latest 重试)+ go mod tidy + go get genproto@latest → go work sync |
scripts/api-docgen/go.mod |
51 | module 名为 bsm/full/tools/api-docgen(与目录 scripts/api-docgen 不一致);15 个模块 replace 到 ../../module/...;无 bsm-sdk/core replace |
scripts/api-docgen/generate.sh |
7 | GOWORK=off + GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn + go run . |
scripts/api-docgen/generate.ps1 |
10 | 同上(PowerShell),结束后清理这两个环境变量 |
scripts/api-docgen/main.go |
300 | 15 个模块的 pb 空导入 → protoregistry.GlobalFiles → 渲染服务/方法/消息/枚举 → 输出 wiki/api/NN-*.md |
.gitignore |
37 | /.builds/(构建产物,build-all-linux.sh 的工作目录)已忽略 |
实测基线(本机 go1.27.1 windows/amd64,仓库声明 go 1.27.1):
go test -mod=readonly ./pkgs/all/... → ok(config/server 两个包,service 包 no test files)
go test -mod=readonly ./pkgs/ecmall/... → ok(config/server/service 三个包)
gofmt -l pkgs module → module\base\fts\internal\routers\register_test.go
gofmt -l scripts → scripts\api-docgen\main.go
GOWORK=off go build ./... (scripts/api-docgen) → go: updates to go.mod needed; to update it: go mod tidy
command -v protoc protoc-gen-go protoc-gen-go-grpc protoc-gen-grpc-gateway protoc-gen-slc → 全部 MISSING
3. 工作区解析与脚本调用流程
flowchart TD
A["开发者 / 提交前"] --> B["go work sync(README:11,16)"]
B --> C["scripts/verify-workspace.sh | .ps1<br/>gofmt -l pkgs module → go list -m → 逐模块 vet + test"]
C --> D["scripts/build-all-linux.sh<br/>find module/**/go.mod → go build(GOOS=linux)"]
E["依赖升级"] --> F["scripts/update-all.sh<br/>go list -m(20 模块)→ go get -u + tidy → go work sync"]
G["接口变更"] --> H["scripts/generate-protobuf.sh<br/>find module/**/go.mod → protoc 重生成 pb/"]
H --> I["scripts/api-docgen/generate.sh(GOWORK=off + go run .)<br/>读 GlobalFiles 渲染 wiki/api/NN-*.md"]
W["go.work: use 20 项 + replace bsm-sdk/core → ../../bsm-sdk/core"]
W -->|"解析模块图"| C
W -->|"解析模块图"| F
W -.->|"api-docgen 不在 use 列表,GOWORK=off 独立解析"| I
X["仓库外:../bsm-sdk/core 必须以固定名字并列存在"] --> W
流程要点:
- 工作区边界:
go.work:3-24的 20 项 =module/下 18 个 +pkgs/all+pkgs/ecmall。module/ec/supply(只有 README)、module/finance/bill、module/finance/loan(空目录)不在其中。实测go list -m -f '{{.Path}}'返回 20 个模块,与use列表完全一致。 - SDK 替换路径:
go.work:26的../../bsm-sdk/core相对go.work所在目录解析为<parent>/bsm-sdk/core;18 个模块go.mod里的../../../../bsm-sdk/core相对各自模块目录解析到同一位置(本机为D:\work\bsm-sdk\core,实测存在)。两侧一致,但都依赖"两个仓库以固定名字并列检出"这一前提。 api-docgen在工作区外:它不在go.work的use列表里,因此在工作区生效时(默认GOWORK)对它执行go list ./...会直接报pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies;它只能靠generate.sh/generate.ps1里的GOWORK=off单独构建。这也是它不被go list -m覆盖(即不被两个校验脚本与update-all.sh触及)的直接原因。api-docgen的输出位置依赖 CWD:filepath.Abs(filepath.Join("..", ".."))(main.go:57)把"仓库根"定义为当前工作目录的上两级。从scripts/api-docgen运行时正好是仓库根;若从仓库根执行go run ./scripts/api-docgen,输出会落到仓库的上一级目录(本机为D:\work\wiki\api)。
4. 工作区与脚本的安全/正确性要点
| 项 | 现状 | 证据 |
|---|---|---|
| SDK 依赖来源 | 全部 20 个 go.mod 都 require git.apinb.com/bsm-sdk/core v0.2.1 且被 replace 为本地路径 → 构建不校验该模块的 sum,克隆任一仓库单独都构建不了 |
各 go.mod:replace 段;go.work:26 |
go.work.sum 与 go.sum 一致性 |
21 个 go.sum 中没有任何 git.apinb.com/bsm-sdk/core 条目(因被 replace,无需);go.work.sum:89 只残留 v0.2.0/go.mod 的哈希,与 require 的 v0.2.1 不一致 |
grep -rn "bsm-sdk" go.work.sum module/*/*/go.sum pkgs/*/go.sum |
go.sum 规模差异 |
scripts/api-docgen/go.sum 仅 42 行,其余 20 个为 134-491 行 |
wc -l 全量统计 |
| 版本漂移(实测) | api-docgen/go.mod 固定的版本落后于工作区模块:protobuf v1.36.11(工作区 1.36.12)、grpc v1.83.0(1.83.2)、x/net v0.57.0(0.59.0)、x/sys v0.47.0(0.48.0)、x/text v0.40.0(0.42.0)、genproto/googleapis/{api,rpc} 20260807(20260911) |
在 -mod=mod 下 go list 会把上述 6 行改写为工作区版本并给 go.sum 增补 14 行(本次审计已还原,未提交任何改动) |
| 构建是否可用(实测) | GOWORK=off go build ./... 在 scripts/api-docgen 报 updates to go.mod needed; to update it: go mod tidy —— Go 1.16 起默认 -mod=readonly,因此 generate.sh 的 go run . 在当前检出上会以该错误退出 |
generate.sh:5-7;实测输出 |
| 工具链可用性(实测) | 本机 protoc、protoc-gen-go、protoc-gen-go-grpc、protoc-gen-grpc-gateway、protoc-gen-slc 全部缺失;脚本在缺工具时按设计 exit 1 |
generate-protobuf.sh:8-18;command -v 输出 |
| 破坏性操作 1 | build-all-linux.sh 在 trap cleanup EXIT 中无条件 rm -rf -- "${ENTRY_ROOT}"(${WORKSPACE_ROOT}/.builds/.entries,已被 .gitignore:2 忽略) |
build-all-linux.sh:9,40-43 |
| 破坏性操作 2 | generate-protobuf.sh 先 mkdir -p,再 find ... -delete 删除 pb/ 下的 *.pb.go / *_grpc.pb.go / *.pb.gw.go,随后才跑 protoc;protoc 失败时 pb/ 只剩手工文件,无备份/回滚(只能靠 git) |
generate-protobuf.sh:31-33,39-47 |
| 批量改写 | update-all.sh 对 20 个模块执行 go get -u ./... + go mod tidy,最后 go work sync(会回写各模块 go.mod),全程无确认,仅在结尾提示"审阅后再提交" |
update-all.sh:33-54 |
| 注释与代码顺序矛盾 | 注释称"go mod tidy 之后会移除该 requirement",但代码顺序是 go mod tidy(:44)→ go get genproto@latest(:48),其后没有 tidy → 该 requirement 会留在最终结果里 |
update-all.sh:44-48 |
| gofmt 门禁覆盖面 | 只检查 pkgs module 两个目录,scripts/ 不在其中;实测 gofmt -l scripts 报 scripts/api-docgen/main.go(该文件工作区行尾为 mixed) |
verify-workspace.sh:10、verify-workspace.ps1:10;git ls-files --eol scripts/api-docgen/main.go |
| 行尾符 | 无 .gitattributes;本机 core.autocrlf=true。实测 958 个已跟踪 .go 文件中有 1 个工作区为 CRLF 的文件(module/base/fts/internal/routers/register_test.go),gofmt -l pkgs module 会报它 → verify-workspace.ps1:11-13 会直接 throw |
ls -a(无 .gitattributes)、git ls-files --eol、gofmt -l pkgs module |
| 模块空集合 | update-all.sh 有校验(:21-24);两个 verify-workspace 脚本没有,go list -m 失败时循环体为空、脚本仍以 0 退出 |
verify-workspace.sh:16-26、verify-workspace.ps1:15-30 vs update-all.sh:20-24 |
未执行 git diff --check |
README 的提交约定要求"运行 workspace 验证脚本和 git diff --check",但两个脚本都不包含该检查 |
README.md:317;两个脚本全文 |
| 平台声明 | README 标注 verify-workspace.sh 支持 Linux/macOS,但脚本用了 mapfile(bash 4+,macOS 自带 bash 3.2 无此内建)、find -print0 | sort -z(GNU sort)、(( config_count += 1 )) 等 GNU 专属写法 |
README.md:14-17;verify-workspace.sh:16、build-all-linux.sh:30、generate-protobuf.sh:35,59、update-all.sh:20 |
| CI 集成 | 仓库没有任何 CI 配置(无 .github/、.gitlab-ci.yml、Makefile,git ls-files 中的 *.yml/*.yaml 全是模块业务配置)→ 全部脚本均为手工执行,wiki/api/*.md 由人工运行 api-docgen 后提交 |
git ls-files | grep -iE "gitlab|github|jenkins|Makefile" 无命中;wiki/api/ 18 个文件已入库 |
| 文档集不同步点 | api-docgen/main.go:38-54 只登记 15 个模块(无 fts/logs/mgt),而 wiki/api/ 实际有 18 个文档(含 00-overview.md、16-fts-rest.md、17-logs-rest.md、18-mgt-rest.md)→ 后 4 个是手工维护,重跑生成不会更新它们 |
main.go:38-54;ls wiki/api/ |
| 冲突检测被静音 | generate.sh:6 / generate.ps1:3 设置 GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn,恰好屏蔽了 README 明令禁止的问题("protobuf 全限定名称必须跨模块唯一");api-docgen 一次性导入 15 个模块的 pb 包,是最可能触发注册冲突的场景 |
generate.sh:6、generate.ps1:3、main.go:12-26、README.md:316 |
5. 审计发现
5.1 安全
| 级别 | 位置 | 问题 |
|---|---|---|
| 中 | go.work:26 + 20 个 go.mod 的 replace |
依赖来源是仓库外相对路径,构建可复现性依赖目录布局。git.apinb.com/bsm-sdk/core 被替换为 ../../bsm-sdk/core(go.work 相对上级目录)与 ../../../../bsm-sdk/core(各模块相对自身);两处最终指向同一目录(本机实测 D:\work\bsm-sdk\core 存在,因此当前不是坏路径),但意味着:单独克隆 bsm-infra/full 无法构建;同级目录被改名或 SDK 换成 git 依赖后,go.sum 中没有任何 bsm-sdk/core 哈希(21 个 go.sum 全无命中,只有 go.work.sum:89 的 v0.2.0/go.mod 残留),无法校验完整性。这是唯一不能靠"改配置项"消除的结构性前提,本次只记录事实与影响,不建议改造依赖方式。 |
| 中 | scripts/update-all.sh:33-49 |
对 20 个模块执行无确认的批量依赖升级。go get -u ./... 会把全部依赖升到最新 minor/patch,随后 go mod tidy 与 go work sync 回写每个模块的 go.mod/go.sum。脚本没有任何 --dry-run、差异预览或交互确认,只有一个结尾提示(:57)。在"依赖升级会同时影响 20 个模块 + 2 个聚合入口 + 1 个工具 module"的仓库里,这是一次高爆炸半径的写操作。 |
| 中 | scripts/generate-protobuf.sh:31-33 |
先删后生成,无备份。find "${output_dir}" -maxdepth 1 -type f \( -name '*.pb.go' -o -name '*_grpc.pb.go' -o -name '*.pb.gw.go' \) -delete 在 protoc 之前执行;set -e 下 protoc 中途失败会留下"生成物已删、新代码未生成"的中间态。手工文件(4 个模块的 pb/blocks_compat.go)因不匹配删除模式而幸存(已实测存在),但该删除模式的白名单性质也意味着新增的生成物命名(如 *.pb.validate.go)不会被清理,会与旧文件长期共存。 |
| 低 | scripts/build-all-linux.sh:40-43 |
trap cleanup EXIT + rm -rf -- "${ENTRY_ROOT}" 是对 .builds/.entries 的无条件递归删除。目标目录在 .gitignore:2 覆盖范围内(/.builds/),爆炸半径可控;但脚本对"ENTRY_ROOT 为空或指向异常"没有任何防御(例如变量被环境覆盖时)。 |
| 低 | README.md:286 与仓库实际内容 |
README 声明"仓库内 YAML 仅为结构示例,凭据使用 CHANGE_ME",但 module/base/logs/etc/logs_dev.yaml:7 是明文达梦连接串(含口令与内网 IP 172.21.138.165:5236)。该文件属模块配置、不在本次逐文件范围,但作为"工作区凭据卫生"的声明与实际不符,记录于此。 |
5.2 正确性与逻辑缺陷
| 级别 | 位置 | 问题 |
|---|---|---|
| 高 | scripts/api-docgen/generate.sh:5-7、generate.ps1:2-5 |
该工具在当前检出下无法运行。GOWORK=off 后 api-docgen/go.mod 需要更新(实测 go: updates to go.mod needed;Go 1.16 起默认 -mod=readonly,不会自动改写),因此 go run . 直接失败。要跑通必须先 go mod tidy(会改写 scripts/api-docgen/go.mod/go.sum,进而产生版本漂移),或把该 module 纳入工作区。 |
| 高 | scripts/api-docgen/go.mod:21-31 |
版本与工作区长期漂移。实测在 -mod=mod 下会被改写 6 行:protobuf 1.36.11→1.36.12、grpc 1.83.0→1.83.2、x/net 0.57.0→0.59.0、x/sys 0.47.0→0.48.0、x/text 0.40.0→0.42.0、genproto/googleapis/{api,rpc} 20260807→20260911。漂移之所以必然:update-all.sh 用 go list -m(update-all.sh:20)取模块,而 api-docgen 不在 go.work,永远不会被升级。 |
| 中 | scripts/verify-workspace.sh:16-26、verify-workspace.ps1:15-30 |
"模块列表为空"被静默当成成功。sh 版用进程替换 < <(go list -m ...),go list 失败不会触发 set -e(进程替换的退出码不参与判断),mapfile 得到空数组后循环体不执行,脚本以 0 退出并打印 Workspace verification completed successfully.;ps1 版同样不检查 go list 的退出码。对比 update-all.sh:21-24 明确做了空集合校验,三份脚本行为不一致。 |
| 中 | scripts/verify-workspace.sh:10、verify-workspace.ps1:10 |
gofmt 门禁不覆盖 scripts/,而 gofmt -l scripts 实测会报 scripts/api-docgen/main.go(工作区行尾为 mixed)→ 同一仓库里"被门禁覆盖的文件都干净、没被覆盖的文件不干净",门禁的语义变得不可预期。 |
| 中 | 无 .gitattributes + core.autocrlf=true(本机) |
行尾符会让 gofmt 门禁在本机直接失败。实测 958 个已跟踪 .go 文件中有 1 个工作区为 CRLF(module/base/fts/internal/routers/register_test.go,索引为 LF、工作区为 CRLF),gofmt -l pkgs module 会输出该文件 → verify-workspace.ps1:11-13 throw、verify-workspace.sh:11-14 退出 1。仓库没有 .gitattributes 固化行尾,因此该结果取决于检出环境。 |
| 中 | scripts/update-all.sh:44-48 |
注释与代码顺序矛盾。注释称"go mod tidy 之后会在不需要时移除这个 requirement",但代码是 go mod tidy(:44)之后才 go get google.golang.org/genproto@latest(:48),且没有后续 tidy → 与注释描述的效果相反,最终 go.mod 会保留这条额外 requirement。 |
| 中 | scripts/build-all-linux.sh:7,30 |
构建范围只有 module/。MODULE_ROOT="${WORKSPACE_ROOT}/module",模块用 find "${MODULE_ROOT}" 收集 → pkgs/all 与 pkgs/ecmall 永远不被构建,${module_name}_prod.yaml 的收集循环(:95-102)同样不覆盖 pkgs/*。聚合入口既没有生产配置(两个 pkgs/*/etc/ 只有 default_dev.yaml),也不在任何脚本的构建路径里。 |
| 中 | scripts/build-all-linux.sh:55-57 |
cmd/main.go 分支是死代码。实测 module/*/*/cmd/main.go 与 module/*/*/*/cmd/main.go 均不存在(全部入口都是 cmd/main/main.go),该分支不会被触发;同时"找不到入口就 exit 1"(:58-61)会让任何新增的"无独立入口"模块直接中断整条构建,而不是跳过。 |
| 低 | scripts/build-all-linux.sh:68,73-74 |
CRLF 处理不对称。读取 module 名后显式去掉 \r(:74,说明作者遇到过 CRLF),但同一段里从 ${entry_file} 里 awk 出来的 package_name(:68)没有同样处理;若 awk 未按文本模式剥离 \r,"main\r" != "main" 会走错分支,去合成一个 import 不存在包的 wrapper。当前 Git Bash 的 awk 文本模式掩盖了该问题。 |
| 低 | scripts/api-docgen/main.go:57 |
输出目录由 CWD 决定:filepath.Abs(filepath.Join("..", "..")) 把仓库根定义为 CWD 的上两级。只有从 scripts/api-docgen 目录运行时才写到 wiki/api/;从仓库根执行会在仓库上一级创建 wiki/api 并写入 15 个文档,不报错、不提示。 |
| 低 | scripts/api-docgen/main.go:38-54 与 wiki/api/ |
生成的 15 个文件与手工维护的 4 个文件混放:wiki/api/ 有 18 个文档,其中 00-overview.md、16-fts-rest.md、17-logs-rest.md、18-mgt-rest.md 不在 modules 表内(fts/logs/mgt 无 proto),重跑生成只覆盖 01-15,剩余 4 个永远靠人工更新,没有任何校验提示它们已过期。 |
| 低 | scripts/update-all.sh:20-24 与 verify-workspace.* |
三个脚本都以 go list -m 作为"模块发现"的唯一来源,而该命令的结果随 GOWORK 是否生效而不同:工作区生效时返回 20 个模块(不含 api-docgen),GOWORK=off 时只返回当前模块。脚本没有显式声明期望(update-all.sh 依赖 go.work 的存在检查 :15-18,两个 verify-workspace 则完全没有该检查)。 |
5.3 未完成/不一致
| 级别 | 位置 | 问题 |
|---|---|---|
| 中 | scripts/api-docgen/go.mod:1 |
module 路径与目录不一致:module 名是 bsm/full/tools/api-docgen,物理位置是 scripts/api-docgen,README 与相关文档也都写 scripts/api-docgen。包路径里不存在 tools/ 目录,属命名遗留;grep/go list 之外的人工检索容易被误导。 |
| 中 | 全体脚本 | 没有任何 CI 接入。仓库无 .github/、无 .gitlab-ci.yml、无 Makefile(git ls-files 校验),README.md:290-299 的"常用脚本"表全部靠人工执行;因此"门禁"实际只在使用者主动运行时生效,wiki/api/*.md 与 go.mod 漂移都只能靠人发现(本次审计的 api-docgen 构建失败正是这种情况)。 |
| 中 | scripts/api-docgen/generate.sh:6、generate.ps1:3 |
静音了项目明令禁止的冲突。GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn 会把 protobuf 全限定名重复注册的致命错误降级为警告,而 README.md:316 的开发约定是"protobuf 全限定名称必须跨模块唯一",main.go:12-26 又恰好一次性导入 15 个模块的 pb 包——这是最应该暴露该冲突的地方,却被显式关掉了。 |
| 低 | README.md:282 |
文档把 SDK 相对路径描述为"准备开发环境时需保证该 SDK 路径存在",但没有给出目录布局要求(必须以 bsm-sdk/core 与 bsm-infra/full 的名字并列)。go.work:26 与 20 处 go.mod replace 是硬约束,README 未量化。 |
| 低 | go.work.sum:89 |
残留 bsm-sdk/core v0.2.0/go.mod 而 require 已是 v0.2.1:因该模块被 replace 为本地路径,构建不使用该 sum(因此不会造成构建失败),但说明 go.work.sum 未随 SDK 版本升级同步,读数会误导排查者。 |
| 低 | scripts/generate-protobuf.sh:9-11 |
GENERATE_SLC 默认 1,因此默认路径依赖 protoc-gen-slc 这个仓库外、未随仓库提供的插件;本机实测 5 个 proto 工具全部缺失,脚本会停在工具预检(行为正确但说明该脚本对环境的隐式要求很高:protoc + 4 个插件 + 正确的 --proto_path)。 |
5.4 健壮性与可维护性
| 级别 | 位置 | 问题 |
|---|---|---|
| 中 | verify-workspace.sh:16,24、build-all-linux.sh:30、generate-protobuf.sh:35,59、update-all.sh:20 |
GNU 专属语法与 README 的双平台声明不符:mapfile/mapfile -d ''(bash 4+)、sort -z、find -print0 | sort -z 在 macOS 默认环境(bash 3.2 + BSD sort)不可用,而 README 明确把 verify-workspace.sh 标为"Linux/macOS"。Build/generate/update 三个脚本文件名未标平台,但同样受影响。 |
| 低 | scripts/generate-protobuf.sh 全文 |
不清空则重复生成的行为未定义;生成后没有 gofmt/go build 校验,也没有"生成物与 proto 是否一致"的检查,pb/ 与 proto/ 脱节只能等编译或运行期暴露。 |
| 低 | scripts/build-all-linux.sh:95-102 |
生产配置收集只按 {basename}_prod.yaml 命名约定匹配(实测 18 个模块全部存在该文件,故当前收集数为 18);一旦某模块改了命名规范,脚本会静默跳过([[ -f ... ]] || continue),只靠最后的计数打印(:104)间接暴露。 |
| 低 | scripts/update-all.sh:35-43 |
go get -u ./... 失败后的重试逻辑只针对 genproto 一种失败原因(注释里说明了),但实现是"任何失败都重试一次 genproto 路径",网络/私有源类失败会被误判并重复执行一次全量 go get -u,时间成本翻倍且错误信息被覆盖。 |
| 低 | scripts/verify-workspace.sh:23-24 |
逐模块 go vet/go test 各自在子 shell ( cd -- "${module_dir}" ) 中执行,失败后 set -e 会中断在当前模块,但不打印"已完成/未完成哪些模块"的汇总;20 个模块的遍历进度只能从 ==> ${module_dir} 行推断。 |
| 低 | scripts/api-docgen/main.go:296-300 |
must() 用 panic 处理所有错误(含建目录、写文件失败);作为一次性工具可接受,但失败时只输出 panic 栈、不指明是哪个模块/哪个输出文件。 |
| 低 | 模块级 go.sum 与 go.work.sum 的维护 |
update-all.sh 结束时执行 go work sync(:54)会同步工作区构建列表到各模块 go.mod,但没有类似的"校验所有 go.sum 是否齐备"的步骤;api-docgen 的 42 行 go.sum 就这样长期缺席于工作区的一致性维护之外。 |
6. 风险汇总
| 编号 | 级别 | 问题 | 影响面 |
|---|---|---|---|
| W1 | 高 | api-docgen 在当前检出下无法构建(updates to go.mod needed),且不在工作区与任何脚本的覆盖范围内 |
文档生成能力失效(wiki/api 只能手工维护) |
| W2 | 高 | api-docgen 依赖版本与工作区长期漂移(protobuf/grpc/x-net/x-sys/x-text/genproto 共 6 处) |
生成结果与运行时 protobuf 版本不一致、排查成本 |
| W3 | 中 | go.work 与 20 处 go.mod 把 SDK 指向仓库外固定相对路径,且 go.sum 无该校验和 |
构建可复现性、独立克隆不可用 |
| W4 | 中 | update-all.sh 无确认地批量升级 20 个模块依赖并回写 go.mod/go.sum |
依赖升级的可控性 |
| W5 | 中 | generate-protobuf.sh 先删生成物再生成,失败无回滚 |
生成中断后的仓库中间态 |
| W6 | 中 | verify-workspace.* 对"模块列表为空"静默成功、gofmt 不覆盖 scripts/、无 .gitattributes 导致 CRLF 文件必然失败 |
门禁可信度 |
| W7 | 中 | 无任何 CI 接入,所有脚本与文档生成全靠人工 | 门禁的实际效力 |
| W8 | 中 | GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn 屏蔽了 README 明令禁止的跨模块注册冲突 |
跨模块 protobuf 命名约束失效 |
| W9 | 中 | build-all-linux.sh 只覆盖 module/,聚合入口无构建路径(叠加其无 prod 配置) |
聚合入口的发布路径 |
| W10 | 中 | GNU 专属语法与 README 的 macOS 支持声明不符 | 跨平台可用性 |
| W11 | 低 | update-all.sh 注释与代码顺序矛盾、genproto 重试逻辑过宽、构建脚本 cmd/main.go 死分支与 CRLF 不对称、api-docgen 输出目录依赖 CWD、wiki/api 生成物与手工文件混放、go.work.sum 残留旧版本 |
可维护性 |
7. 修复建议(务实项)
- 让
api-docgen能跑、且不再漂移(W1、W2):二选一——① 把scripts/api-docgen加入go.work的use列表并删掉generate.sh/generate.ps1里的GOWORK=off(这样它会被go list -m覆盖,自动纳入update-all.sh与verify-workspace.*);或 ② 保持独立 module,但把这些版本对齐到工作区当前值并加上"先go mod tidy再go run ."的显式步骤。推荐 ①,改动最小且一次性消除漂移。 - 修门禁的三个洞(W6):
verify-workspace.sh|ps1的 gofmt 目标从pkgs module扩为pkgs module scripts;在两个脚本的go list -m之后加"模块数为 0 则报错退出"(照抄update-all.sh:21-24);补一个.gitattributes把*.go、*.sh、*.ps1、*.psm1固定为text eol=lf,消除 CRLF 造成的 gofmt 假阳性。 - 给
go.work的 SDK 路径加一道自检(W3):在verify-workspace.*开头加一条"go list -m git.apinb.com/bsm-sdk/core失败则提示目录布局要求并退出"的检查,把"路径不存在"从 20 个模块的编译错误变成一条明确提示;同时把 README:282 的布局要求写成具体路径(../bsm-sdk/core与../../../../bsm-sdk/core两个基准目录)。 - 给批量依赖升级加一道确认(W4):
update-all.sh增加REQUIRE_CONFIRM/--yes之类的开关,默认先打印将改写的go.mod列表再要求确认;不需要 dry-run 框架,一个read -r即可。同时修正:44-48的注释与代码顺序(把go get genproto@latest放到go mod tidy之前,或删掉注释里"tidy 会移除"的说法)。 - protoc 生成改为"先生成到临时目录、成功后替换"(W5):把
--go_out等输出到一个临时目录,全部成功后整体替换pb/;或者至少把find ... -delete移到 protoc 成功之后(当前顺序是删除在前)。顺带把删除模式从"后缀白名单"改为"仅保留非生成文件的显式白名单",避免新增生成物残留。 - 扩展构建范围(W9):
build-all-linux.sh增加对pkgs/的扫描(聚合入口产出all/ecmall两个二进制并收集default_prod.yaml);同时删掉不可能命中的cmd/main.go分支(:55-57),把"找不到入口"从exit 1改为可配置的跳过,并统一package_name与module_name的\r处理。 api-docgen的 CWD 依赖与文档集(W11):把main.go:57的根目录改为基于go list -m -f '{{.Dir}}'或可配置的输出目录参数(不引入新依赖,一次flag.String即可);在生成的目录里用文件头注释标注"本文件由 api-docgen 生成",把手工维护的 00/16/17/18 与生成物在文件名或注释上区分开。- 恢复注册冲突检测(W8):把
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn去掉(恢复默认的 fail-fast),让README.md:316的约定真正生效;若确有合法的重复注册,应在源头修正命名而不是全局降级。 - 跨平台兼容(W10):要么把 README 的平台声明改为"Linux + Git Bash/WSL",要么把
mapfile/sort -z替换为 POSIX 写法(while IFS= read -r+find ... -print | sort);后者改动集中在 4 个脚本、不超过十几行。 - 补最小 CI(W7):即使不引入完整流水线,也可以在仓库内加一个"提交前执行
scripts/verify-workspace.sh"的钩子说明或最简 CI 任务(运行同一个脚本即可,无需新增脚本)。这一步只是让已有门禁真正生效,不新增框架或抽象层。
本报告只列与现有脚本/工作区定义直接相关的修复项,不引入新的构建系统、包管理抽象或 CI 框架。审计中"引入统一 Makefile/任务编排器""把脚本重写为 Go 工具""给工作区引入私有 module proxy"一类改造不在此列。