Macro 仓库 Rust 代码清理实战:从 `.pi/prompts/rust_clean.md` 看 `prepare_db`、`clippy`、`check`、`format` 四步质量门禁

发布时间:2026/10/30 4:41:23
Macro 仓库 Rust 代码清理实战:从 `.pi/prompts/rust_clean.md` 看 `prepare_db`、`clippy`、`check`、`format` 四步质量门禁
  • 后端
  • 前端
  • 协同办公
  • 即时通讯
  • 企业应用
  • CRM
  • AI Agent
  • 人工智能

【免费下载链接】macro

Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.

项目地址:https://gitcode.com/GitHub_Trending/macro3/macro
点击查看免费下载

crates/、services/和tooling/是 Macro 仓库 Rust 代码的主要所在地。本文以仓库内提示词文件.pi/prompts/rust_clean.md描述的四步清理流程为主线,逐条拆解just prepare_db、just clippy、just check、just format背后的真实实现、执行顺序约束与失败处理策略,并结合源码给出可复现的排错路径。

一、原始流程:一条严格串行的清理指令

.pi/prompts/rust_clean.md的内容非常克制,它只定义了三件事:作用范围、命令顺序、失败处理。

You will clean up the Rust code in `crates/`, `services/`, and `tooling/`. From the repository root, run the following commands in order: 1. `just prepare_db` 2. `just clippy` 3. `just check` 4. `just format` Note: If any of the above tests fail make sure to always run `just format` after any code is updated If any command fails surface that failure to the user and do not continue running commands.

这四行命令并非随便排列,而是由三个隐含约束决定的:

  1. SQLx 离线缓存必须先刷新。仓库的 Rust 检查全部以SQLX_OFFLINE=true运行,若缓存与查询文本不一致,后续命令会在编译期宏展开处报错,而不是在 lint 阶段报错。
  2. 静态检查先于格式化。lint 失败会驱动你改代码,改完代码又要重新跑一遍前缀命令,因此格式化放在最后可以避免反复格式化。
  3. 失败即停,不许静默降级。提示词明确要求向用户暴露失败,而不是换一条“更宽松”的命令绕过。

下面逐项展开。

二、第 1 步:just prepare_db—— 刷新 SQLx 离线查询缓存

prepare_db定义在 tooling/just/rust.just,并委托给 SQLx 的具体实现:

# This can be used in any crate that uses macrodb under the hood [no-cd] prepare_db: just sqlx::prepare_db {{ DATABASE_URL }}

这代表真正的执行体在 tooling/just/sqlx.just:

[no-cd] prepare_db $DATABASE_URL *FLAGS: cargo sqlx prepare --workspace -- {{ FLAGS }} --workspace --exclude sync_service --all-features

从这里可以读出三个关键设计:

  • --workspace --all-features:缓存覆盖整个 workspace 的功能组合。Macro 大量使用 Cargo feature 来切换 inbound/outbound 适配器(例如crates/soup/Cargo.toml中的ports、outbound、axum等 feature),只在默认 feature 下生成缓存会产生大量“缺缓存条目”的假性错误。
  • --exclude sync_service:services/sync-service是编译到 wasm 的 Cloudflare Worker(见 services/sync-service/AGENTS.md),其依赖闭包与传统服务不同,因此从 workspace 级离线缓存中排除。与之对应,clippy与rust-check都对sync_service单独跑一遍。
  • ***** **DATABASE_URL来源**:默认值定义在 [tooling/just/database.just](https://link.gitcode.com/i/958f49c3abbaff3e20601f2e2c9e3c58),即postgres://user:password@localhost:5432/macrodb`。

为什么这一步必须最先执行

cargo sqlx prepare会连接真实数据库、解析 SQL 并写出.sqlx/query-*.json缓存。当前仓库的.sqlx/目录下有 3000 多个缓存文件,全部纳入版本控制。只要改动涉及sqlx::query!/query_as!/query_scalar!等编译期宏,缓存就与代码脱节,SQLX_OFFLINE=true的后续编译会直接失败。

这一步与 .pi/agents/db-checker.md 的职责边界一致:DB reviewer 子代理被明确限制“唯一允许改文件的命令就是just prepare_db”,且仅在 SQLx 查询发生变化时触发。同一规则也写在 .pi/skills/sqlx-query-validator/SKILL.md 中——如果本次清理没有触碰任何查询,这一步可以被跳过;一旦触碰,就必须执行并把生成的.sqlx变更加入提交。

前置条件:本地 Postgres 必须已启动并完成迁移。可按 docs/development/database.md 执行just run_dbs -d、just setup_test_envs、just setup_macrodb。

三、第 2 步:just clippy—— 以警告为错误的 workspace lint

clippy是整条流水线里最重的一步,定义为四条串行命令(tooling/just/rust.just):

clippy: RUSTFLAGS="-Dwarnings -Dclippy::disallowed_methods" RUSTDOCFLAGS=-Dwarnings CARGO_TERM_COLOR=always SQLX_OFFLINE=true cargo clippy --workspace --all-features --exclude sync_service RUSTFLAGS="-Dwarnings -Dclippy::disallowed_methods" RUSTDOCFLAGS=-Dwarnings CARGO_TERM_COLOR=always SQLX_OFFLINE=true cargo clippy -p sync_service -- -A clippy::unnecessary_map_or -A clippy::collapsible_if RUSTFLAGS="-Dwarnings -Dclippy::disallowed_methods" RUSTDOCFLAGS=-Dwarnings CARGO_TERM_COLOR=always SQLX_OFFLINE=true cargo clippy -p soup --no-default-features RUSTFLAGS="-Dwarnings -Dclippy::disallowed_methods" RUSTDOCFLAGS=-Dwarnings CARGO_TERM_COLOR=always SQLX_OFFLINE=true cargo clippy -p soup --no-default-features --features ports

理解这条 recipe 需要注意四个细节。

1.-Dwarnings:警告即失败

所有四条命令都设置RUSTFLAGS="-Dwarnings",并额外设置RUSTDOCFLAGS=-Dwarnings。这意味着文档注释中的失效链接、无效 HTML 也会让 lint 失败,清理时不能只关注cargo输出的warning:行,还要看rustdoc报错。

2.-Dclippy::disallowed_methods:项目级禁用 API

真正让这条命令具备“仓库专属语义”的是 clippy.toml:

disallowed-methods = [ { path = "std::env::var", reason = "use macro_env_var so APP_SECRETS_JSON is honored" }, { path = "sqlx::query", replacement = "sqlx::query!" }, { path = "sqlx::query_as", replacement = "sqlx::query_as!" }, { path = "sqlx::query_scalar", replacement = "sqlx::query_scalar!" }, ]

由此可以确认两条硬性约定:

  • 读取环境变量不能用std::env::var,必须走macro_env_var,否则APP_SECRETS_JSON机制会被绕过;
  • 查询不能直接调用sqlx::query/query_as/query_scalar函数,必须使用对应宏。

第二条与 .pi/skills/sqlx-query-validator/SKILL.md 的检查清单完全对应:只有“SQL 必须动态拼装、无法用宏表达”时才允许直接调用函数,且动态片段必须来自受信任白名单,取值必须用.bind(...)绑定。清理代码时若为了消除类型推断错误而把宏改成函数调用,会在这一步被直接拦下。

3.--exclude sync_service与单独补偿

sync_service因 wasm 目标被排除在 workspace 级命令之外,但并没有被放过:第二条命令单独对它执行 clippy,并显式放行clippy::unnecessary_map_or与clippy::collapsible_if。这是“排除范围但不排除责任”的写法,清理时不要误以为 sync-service 不受约束。

4.soup --no-default-features的最小特性矩阵

后两条命令针对crates/soup,分别验证“无默认特性”和“仅启用ports”两种组合。结合 crates/soup/Cargo.toml 可见,soup的outbound、inbound、axum、ai_tools各自拉入不同的域 crate 与可选依赖,默认特性全开时很多被条件编译屏蔽的代码根本不会被检查。这两条命令等于强制检查“最精简的下游使用者”视角。

四、第 3 步:just check—— 只审视你改动的文件

just check定义在 tooling/just/check.just,与clippy有本质区别:它不是全量检查,而是变更范围检查。

范围计算逻辑如下:

base="${CHECK_BASE:-$(git merge-base HEAD origin/main 2>/dev/null || true)}" changed=$({ [ -n "$base" ] && git diff --name-only "$base"; \ git diff --name-only HEAD; \ git ls-files --others --exclude-standard; } | sort -u \ | while IFS= read -r f; do [ -f "$f" ] && printf '%s\n' "$f"; done)

即“与origin/main的 merge-base 差异 + 未提交改动 + 未跟踪文件”三者并集。文件集为空时直接输出no changed files — nothing to check并以 0 退出。

Rust 文件触发的动作

若改动集合中包含crates/、services/、tooling/下的.rs文件,会执行:

cargo fmt --check || { echo " fix: cargo fmt"; fail=1; }

注意这里只做格式校验,不做格式化。这正是原提示词要求“最后统一just format”的原因:在未格式化的中间状态下运行just check,预期会看到fix: cargo fmt。

其他变更会同时被检查

just check是一个多语言门禁,Rust 只是其中一支。从源码可见它还会:

变更类型检查动作
apps/web、packages等目录的.ts/.tsxBiomeci(分别使用 web 与 SDK 各自的配置),随后 oxlint
apps/web的.ts/.tsxoxlint--type-aware,唯一 error 规则为typescript/no-floating-promises
static_assets/schema.graphqlcargo x graphql-schema-compat --base <base>
宽范围.rs/.ts/.tsxast-grep 规则扫描,规则 id 映射到 docs/development/style-guide.md

这意味着即便你的清理只改 Rust,只要工作区里还残留未提交的前端改动,它们也会一起被检查。若只想聚焦 Rust,提交或清理无关改动是更省时的做法。

just check full与CHECK_BASE

check.just的注释给出了两种用法:

  • just check:秒级,覆盖 rustfmt + biome + oxlint + ast-grep;
  • just check full:额外加跑tsc与clippy(分钟级)。

此外,浅克隆场景下git merge-base可能取不到值,此时范围会静默收窄为“仅未提交改动”。源码对此有明确提示,并提供了覆盖方式:

CHECK_BASE=origin/main just check

五、第 4 步:just format—— 收尾且必须放在最后

format: SQLX_OFFLINE=true cargo fmt

只有一行,但它是整条流水线唯一“写文件”的步骤。原提示词特别强调:任何命令失败并被修复后,都要重新运行just format。原因是cargo fmt依据rustfmt规则整文件重排,而前面的 lint 修复往往是在局部插入?、调整if let结构,格式会因此再次漂移。

SQLX_OFFLINE=true在此处并非必需(cargo fmt不编译),但与仓库其他 recipe 保持一致,避免在format之后误跑了依赖在线数据库的命令而混淆环境。

六、把四步串起来:顺序为何不能互换

顺序命令作用若提前/推后会发生什么
1just prepare_db用真实 DB 重建.sqlx缓存放到clippy之后:宏展开失败,报“query not found in cache”一类错误
2just clippy全量 lint,警告即错误放到check之后:check只查改动文件,会漏掉被改动代码影响的兄弟 crate
3just check变更范围内的格式/lint/规则放到format之后:失去“格式问题会被暴露”的意义
4just format统一写入格式放到最前:lint 修复会再次破坏格式,需要重复执行

原提示词的失败语义同样重要:任一命令失败就向用户报告并停止。这与 .pi/skills/rust-validation/SKILL.md 的规则一致——“如果rust-check或clippy失败,不要继续执行后续命令;修复后从序列开头重跑”。

七、与仓库其他质量门禁的关系

.pi/prompts/rust_clean.md是清理场景的收敛版,仓库里还存在两条更完整的序列,理解它们的差异可以避免误判“检查已全部通过”。

1.rust-validation技能:类型检查 + hakari

.pi/skills/rust-validation/SKILL.md 给出的顺序是:

just rust-check just clippy just format just hakari

其中just rust-check是 workspace 类型检查(tooling/just/rust.just),just hakari会运行cargo run -p xtask -- deps重新生成依赖统一元数据(tooling/just/rust.just,配置见 .config/hakari.toml)。后者仅在改动Cargo.toml时才有意义——hakari.toml中维护了大量traversal-excludes,用于把 wasm 目标 crate 与 dev-only 的 xtask 排除在workspace-hack之外。

清理任务若只改函数体,clippy已隐含完成编译;但若动过依赖声明或 feature 组合,应补跑rust-check与hakari。

2.rust.md中的推荐组合

docs/development/rust.md 明确了各命令定位:

命令用途
just rust-check使用 SQLx 缓存对 workspace 做类型检查
just check快速、变更范围的格式/lint/规则门禁,不是类型检查或测试运行器
just check full增加 TypeScript 检查与 Rust clippy
just clippy使用 SQLx 缓存跑 workspace lint
just format格式化

并特别提醒:SQLX_OFFLINE=true只适用于cargo check、cargo build、cargo clippy,不要在会运行测试的会话中导出;测试必须连活库,且POST前应unset SQLX_OFFLINE。

八、常见失败与定位

现象根因处理
prepare_db连接失败本地 Postgres 未启动或未迁移按 docs/development/database.md 启动并迁移后重跑
宏展开报“缓存中找不到查询”改了查询但未刷新缓存重跑just prepare_db,并提交.sqlx变更
clippy 报use of a disallowed method使用了std::env::var或直接调用sqlx::query*改用macro_env_var/ 宏版本
clippy 在sync_service报 wasm 相关错误该 crate 目标是 wasm32确认未在非 wasm 目标下误改其依赖
just check输出fix: cargo fmt改动的 Rust 文件尚未格式化继续执行第 4 步just format,再重跑just check
just check报“no merge-base”浅克隆或缺origin/main设置CHECK_BASE=<ref>扩大范围
ast-grep 命中规则 id违反 docs/development/style-guide.md 中的机械规则按规则说明改代码,不要改规则

关于最后一条需要额外说明:just check只把severity: error的 ast-grep 规则视为失败,warning/hint 仅打印。清理时可以优先处理 error 级规则,但不应为了“输出干净”去修改rules/ast-grep/下的 YAML——那属于规则变更,不属于代码清理。

九、边界说明

本文只依据.pi/prompts/rust_clean.md与其在仓库中的实现展开。以下几点属于可确认的事实边界,需在实操中自行验证:

  • 文中所有 recipe 内容来自当前仓库快照,若tooling/just/被改动,命令语义可能变化;
  • 仓库未提供根级just test,清理流程本身不包含测试执行(docs/development/rust.md 明确要求按包执行cargo test -p <package>);
  • 环境依赖(Nix dev shell、just、sqlx-cli、cargo)需按 docs/development/rust.md 的说明准备,当前文档未给出可省略的替代方案。

将这四步理解为一条有向流水线,而不是四个独立命令,是.pi/prompts/rust_clean.md真正传达的信息:缓存先行、lint 全量、范围校验、格式收尾,失败即停。

  • 后端
  • 前端
  • 协同办公
  • 即时通讯
  • 企业应用
  • CRM
  • AI Agent
  • 人工智能

【免费下载链接】macro

Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.

项目地址:https://gitcode.com/GitHub_Trending/macro3/macro
点击查看免费下载
上一篇:GDAL 栅格数据集只读线程安全:RFC 101 从 API 到源码实现的完整指南
下一篇:Harbor 网络策略矩阵实战:用 `[verifier.environment]` 构建「Agent 断网 + Verifier 独立公网」的隔离评测任务

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考