ci(guards): 新增 presenter 零 UI 与 design 零第三方依赖两道门禁(M89)
- presenter-no-ui:照 core-no-ui 的「依赖表 + cargo tree」双查,名单一致 (slint/egui/eframe/iced/winit/gtk/javafx/tauri/fltk/druid/relm)。 依赖表检查按表头切出依赖表再查键名——presenter 的 description 合法地写着 「Slint 版与 iced 版共用」,core-no-ui 那种整文件 grep 在这里会误报。 - design-zero-dep:cargo tree -p unlucky-design --edges normal 只允许出现自身一行, 把 README 明写的「零第三方依赖」卖点从人肉守变成 CI 守。 - 两条都 fail-closed:cargo tree 自身失败(crate 改名/解析异常)同样红; 失败信息给出命中清单与放哪里的建议。 - 未改任何已有 job 的语义(core-no-ui / file-size / licenses 原样)。
This commit is contained in:
1 parent
1d0ff70908
commit
56bc52756b
1 file changed
+82
-3
@@ -1,9 +1,11 @@
|
||||
name: Guards
|
||||
|
||||
# 架构与合规守门(与 fmt/clippy 同级,不允许 continue-on-error):
|
||||
# 1. core-no-ui —— crates/core 零 UI 依赖(D3 铁律),依赖表 + cargo tree 双查
|
||||
# 2. file-size —— 全仓代码/文档单文件 ≤400 行(用户硬约束)
|
||||
# 3. licenses —— cargo-deny:许可证白名单 + GPL 依赖拦截 + 供应链来源校验
|
||||
# 1. core-no-ui —— crates/core 零 UI 依赖(D3 铁律),依赖表 + cargo tree 双查
|
||||
# 2. presenter-no-ui —— crates/presenter 零 UI 依赖(D26 / M85):Slint 版与 iced 版共用的宿主层
|
||||
# 3. design-zero-dep —— crates/design 零第三方依赖(M61 / M87):依赖树只允许出现自身
|
||||
# 4. file-size —— 全仓代码/文档单文件 ≤400 行(用户硬约束)
|
||||
# 5. licenses —— cargo-deny:许可证白名单 + GPL 依赖拦截 + 供应链来源校验
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -43,6 +45,83 @@ jobs:
|
||||
fi
|
||||
echo "OK: unlucky-core 依赖树零 UI"
|
||||
|
||||
# presenter 是 Slint 版(crates/app)与 iced 版(crates/app-iced)共用的唯一业务层(D26)。
|
||||
# 一旦 slint/iced/winit 被拉进来,分层就废了,而编译与单测仍会全绿 —— 所以必须由 CI 守。
|
||||
# 与 core-no-ui 同样是「依赖表 + cargo tree」双查,名单保持一致;脚本刻意复制而非抽公共脚本,
|
||||
# 理由是 CI 守门脚本自身挂掉的排查成本远高于这几行重复。
|
||||
presenter-no-ui:
|
||||
name: presenter 零 UI 守门
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: 依赖表直查(crates/presenter/Cargo.toml 的依赖表不得出现 UI 框架)
|
||||
run: |
|
||||
set -uo pipefail
|
||||
forbidden='slint|egui|eframe|iced|winit|gtk|javafx|tauri|fltk|druid|relm'
|
||||
# 与 core-no-ui 的整文件 grep 不同:本 crate 的 description 合法地写着
|
||||
# 「Slint 版与 iced 版共用」,整文件 grep 会把它当越线。所以先按表头切出依赖表
|
||||
# (含 target.*.dependencies 与 dev/build 表),再去掉注释行,只查键名。
|
||||
awk '
|
||||
/^[[:space:]]*\[/ {
|
||||
in_deps = ($0 ~ /^[[:space:]]*\[(target\..*\.)?(dependencies|dev-dependencies|build-dependencies)\][[:space:]]*$/)
|
||||
next
|
||||
}
|
||||
in_deps && !/^[[:space:]]*#/ && !/^[[:space:]]*$/ { printf "%d:%s\n", NR, $0 }
|
||||
' crates/presenter/Cargo.toml > /tmp/presenter-deps.txt
|
||||
cat /tmp/presenter-deps.txt
|
||||
if grep -iE "$forbidden" /tmp/presenter-deps.txt; then
|
||||
echo "::error file=crates/presenter/Cargo.toml::presenter 不得依赖 UI 框架(D26)。窗口创建 / 渲染 / 图片解码属于 app 与 app-iced 侧;需要跨前端共用的类型请放进 presenter 的 dto。"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: crates/presenter/Cargo.toml 依赖表无 UI 依赖"
|
||||
- name: 依赖树泄漏检查(cargo tree -p unlucky-presenter,排除自身行)
|
||||
run: |
|
||||
set -uo pipefail
|
||||
forbidden='slint|egui|eframe|iced|winit|gtk|javafx|tauri|fltk|druid|relm'
|
||||
# 注意:cargo tree 命中 UI 框架时**退出码仍是 0**,所以只能抓输出再判断。
|
||||
if ! tree=$(cargo tree -p unlucky-presenter --edges normal --prefix none); then
|
||||
echo "::error::cargo tree -p unlucky-presenter 执行失败 —— crate 是否仍叫 unlucky-presenter?"
|
||||
exit 1
|
||||
fi
|
||||
echo "$tree"
|
||||
hits=$(printf '%s\n' "$tree" | grep -vE '^unlucky-presenter v' | grep -iE "$forbidden")
|
||||
if [ -n "$hits" ]; then
|
||||
echo "::error::presenter 的依赖树出现 UI 框架(传递依赖也不允许),本次命中:"
|
||||
printf '%s\n' "$hits" | sed 's/^/ - /'
|
||||
echo "::error::presenter 是 Slint 版与 iced 版共用的宿主层:把窗口 / 渲染 / 图片解码留在 app 侧,presenter 只持有与框架无关的 DTO、状态 reducer 与 host 装配。"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: unlucky-presenter 依赖树零 UI"
|
||||
|
||||
# unlucky-design 是「纯数据 + 纯函数、零第三方依赖」的设计令牌层,这是 README 明写的卖点
|
||||
# (连 serde 都不加),但目前只靠人肉守。零依赖的判据比 UI 名单更硬:依赖树只允许有自己一行。
|
||||
design-zero-dep:
|
||||
name: design 零第三方依赖守门
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: 依赖树只允许出现自身(cargo tree -p unlucky-design --edges normal)
|
||||
run: |
|
||||
set -uo pipefail
|
||||
if ! tree=$(cargo tree -p unlucky-design --edges normal --prefix none); then
|
||||
echo "::error::cargo tree -p unlucky-design 执行失败 —— crate 是否仍叫 unlucky-design?"
|
||||
exit 1
|
||||
fi
|
||||
echo "$tree"
|
||||
# 空行不算,剩下的每一行都必须是自己:正常时整个输出只有一行
|
||||
# `unlucky-design v0.1.0 (/path/to/crates/design)`。
|
||||
lines=$(printf '%s\n' "$tree" | grep -cE '[^[:space:]]' || true)
|
||||
others=$(printf '%s\n' "$tree" | grep -vE '^[[:space:]]*$' | grep -vE '^unlucky-design v' || true)
|
||||
if [ -n "$others" ] || [ "$lines" -ne 1 ]; then
|
||||
echo "::error file=crates/design/Cargo.toml::unlucky-design 必须零第三方依赖,本应只有自身一行,实际 $lines 行;越线的包:"
|
||||
printf '%s\n' "$others" | sed 's/^/ - /'
|
||||
echo "::error::design 是纯数据 + 纯函数的设计令牌:色彩 / 排版 / 形状 / 高程 / 动效请不要引入 serde、rand、once_cell 等任何 crate。序列化与运行时能力放到消费方(app / app-iced / presenter)。"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: unlucky-design 依赖树只有自身,零第三方依赖"
|
||||
|
||||
file-size:
|
||||
name: 文件行数 ≤400 守门
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
Reference in new issue
Block a user