adult and update go.mod
This commit is contained in:
179
docs/workspace-and-scripts.md
Normal file
179
docs/workspace-and-scripts.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# 工作区与工程脚本代码审计报告
|
||||
|
||||
| 项 | 内容 |
|
||||
| --- | --- |
|
||||
| 审计对象 | 工作区定义 `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 | 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. 工作区解析与脚本调用流程
|
||||
|
||||
```mermaid
|
||||
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
|
||||
```
|
||||
|
||||
流程要点:
|
||||
|
||||
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`,**随后**才跑 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. 修复建议(务实项)
|
||||
|
||||
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"一类改造不在此列。
|
||||
Reference in New Issue
Block a user