Files
full/docs/workspace-and-scripts.md
2026-09-22 18:53:53 +08:00

180 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工作区与工程脚本代码审计报告
| 项 | 内容 |
| --- | --- |
| 审计对象 | 工作区定义 `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并把私有 SDK `git.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 | bashgofmt 检查 → `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/... → okconfig/server 两个包service 包 no test files
go test -mod=readonly ./pkgs/ecmall/... → okconfig/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. 工作区解析与脚本调用流程
```mermaid
flowchart TD
A["开发者 / 提交前"] --> B["go work syncREADME: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 buildGOOS=linux"]
E["依赖升级"] --> F["scripts/update-all.sh<br/>go list -m20 模块)→ 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.shGOWORK=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
```
流程要点:
1. **工作区边界**`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` 列表完全一致。
2. **SDK 替换路径**`go.work:26``../../bsm-sdk/core` 相对 `go.work` 所在目录解析为 `<parent>/bsm-sdk/core`18 个模块 `go.mod` 里的 `../../../../bsm-sdk/core` 相对各自模块目录解析到**同一位置**(本机为 `D:\work\bsm-sdk\core`,实测存在)。两侧一致,但都依赖"两个仓库以固定名字并列检出"这一前提。
3. **`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` 触及)的直接原因。
4. **`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`**随后**才跑 protocprotoc 失败时 `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. 修复建议(务实项)
1. **让 `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 .`"的显式步骤。推荐 ①,改动最小且一次性消除漂移。
2. **修门禁的三个洞**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 假阳性。
3. **给 `go.work` 的 SDK 路径加一道自检**W3`verify-workspace.*` 开头加一条"`go list -m git.apinb.com/bsm-sdk/core` 失败则提示目录布局要求并退出"的检查,把"路径不存在"从 20 个模块的编译错误变成一条明确提示;同时把 README:282 的布局要求写成具体路径(`../bsm-sdk/core``../../../../bsm-sdk/core` 两个基准目录)。
4. **给批量依赖升级加一道确认**W4`update-all.sh` 增加 `REQUIRE_CONFIRM`/`--yes` 之类的开关,默认先打印将改写的 `go.mod` 列表再要求确认;不需要 dry-run 框架,一个 `read -r` 即可。同时修正 `:44-48` 的注释与代码顺序(把 `go get genproto@latest` 放到 `go mod tidy` 之前,或删掉注释里"tidy 会移除"的说法)。
5. **protoc 生成改为"先生成到临时目录、成功后替换"**W5`--go_out` 等输出到一个临时目录,全部成功后整体替换 `pb/`;或者至少把 `find ... -delete` 移到 protoc 成功之后(当前顺序是删除在前)。顺带把删除模式从"后缀白名单"改为"仅保留非生成文件的显式白名单",避免新增生成物残留。
6. **扩展构建范围**W9`build-all-linux.sh` 增加对 `pkgs/` 的扫描(聚合入口产出 `all`/`ecmall` 两个二进制并收集 `default_prod.yaml`);同时删掉不可能命中的 `cmd/main.go` 分支(`:55-57`),把"找不到入口"从 `exit 1` 改为可配置的跳过,并统一 `package_name``module_name``\r` 处理。
7. **`api-docgen` 的 CWD 依赖与文档集**W11`main.go:57` 的根目录改为基于 `go list -m -f '{{.Dir}}'` 或可配置的输出目录参数(不引入新依赖,一次 `flag.String` 即可);在生成的目录里用文件头注释标注"本文件由 api-docgen 生成",把手工维护的 00/16/17/18 与生成物在文件名或注释上区分开。
8. **恢复注册冲突检测**W8`GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn` 去掉(恢复默认的 fail-fast`README.md:316` 的约定真正生效;若确有合法的重复注册,应在源头修正命名而不是全局降级。
9. **跨平台兼容**W10要么把 README 的平台声明改为"Linux + Git Bash/WSL",要么把 `mapfile`/`sort -z` 替换为 POSIX 写法(`while IFS= read -r` + `find ... -print | sort`);后者改动集中在 4 个脚本、不超过十几行。
10. **补最小 CI**W7即使不引入完整流水线也可以在仓库内加一个"提交前执行 `scripts/verify-workspace.sh`"的钩子说明或最简 CI 任务(运行同一个脚本即可,无需新增脚本)。这一步只是让已有门禁真正生效,不新增框架或抽象层。
> 本报告只列与现有脚本/工作区定义直接相关的修复项,不引入新的构建系统、包管理抽象或 CI 框架。审计中"引入统一 Makefile/任务编排器""把脚本重写为 Go 工具""给工作区引入私有 module proxy"一类改造不在此列。