chore(bazel): generated clippy config from workspace lints at release time

- Added scripts/gen-clippy-bazelrc.ts: emits bazel/clippy.bazelrc from
  [workspace.lints] in Cargo.toml (groups by ascending priority, then
  per-lint overrides, alphabetical within each tier); --check mode
  verifies sync without writing.
- release.ts regenerates it alongside the lockfiles; the release_gate CD
  job runs the --check so drift only blocks publishing, never ordinary
  CI. Added the gen:clippy package script.
This commit is contained in:
can1357
2026-08-20 04:18:26 +02:00
parent ac5500beda
commit 7e54061cbb
5 changed files with 101 additions and 2 deletions
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env bun
/**
* Generates `bazel/clippy.bazelrc` from `[workspace.lints]` in `Cargo.toml` so
* the bazel `clippy-strict` config (applied to crates that opt in via
* `[lints] workspace = true`) can never drift from the cargo lint policy.
*
* Emission mirrors rustc lint-level resolution: `[workspace.lints.rust]`
* entries first (bare rustc lint flags), then clippy entries — negative
* `priority` values (the lint groups) before per-lint overrides, alphabetical
* within each tier.
*
* Usage:
* bun scripts/gen-clippy-bazelrc.ts # rewrite bazel/clippy.bazelrc
* bun scripts/gen-clippy-bazelrc.ts --check # exit 1 when the file is stale
*/
import * as path from "node:path";
type LintLevel = "allow" | "warn" | "deny" | "forbid";
type LintEntry = LintLevel | { level: LintLevel; priority?: number };
type LintTable = Record<string, LintEntry>;
const FLAG_BY_LEVEL: Record<LintLevel, string> = { allow: "A", warn: "W", deny: "D", forbid: "F" };
const repoRoot = path.join(import.meta.dir, "..");
const outputPath = path.join(repoRoot, "bazel", "clippy.bazelrc");
function normalize(entry: LintEntry): { level: LintLevel; priority: number } {
if (typeof entry === "string") return { level: entry, priority: 0 };
return { level: entry.level, priority: entry.priority ?? 0 };
}
/** Lint names ordered by ascending priority tier, alphabetical within a tier. */
function orderedNames(table: LintTable): string[] {
return Object.keys(table).sort((a, b) => {
const pa = normalize(table[a]).priority;
const pb = normalize(table[b]).priority;
if (pa !== pb) return pa - pb;
return a < b ? -1 : a > b ? 1 : 0;
});
}
function flagLines(table: LintTable, prefix: string): string[] {
return orderedNames(table).map(name => {
const { level } = normalize(table[name]);
return `build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-${FLAG_BY_LEVEL[level]}${prefix}${name}`;
});
}
async function render(): Promise<string> {
const cargo = Bun.TOML.parse(await Bun.file(path.join(repoRoot, "Cargo.toml")).text()) as {
workspace?: { lints?: { rust?: LintTable; clippy?: LintTable } };
};
const lints = cargo.workspace?.lints;
if (!lints?.clippy) {
throw new Error("Cargo.toml has no [workspace.lints.clippy] table");
}
const lines = [
"# Generated by scripts/gen-clippy-bazelrc.ts from [workspace.lints] in Cargo.toml.",
"# Do not edit by hand; run `bun run gen:clippy` after changing the lint policy.",
"# Applies only to crates that opt in via `[lints] workspace = true`.",
...flagLines(lints.rust ?? {}, ""),
...flagLines(lints.clippy, "clippy::"),
];
return `${lines.join("\n")}\n`;
}
const expected = await render();
const current = await Bun.file(outputPath)
.text()
.catch(() => "");
if (process.argv.includes("--check")) {
if (current !== expected) {
console.error(
"bazel/clippy.bazelrc is stale relative to [workspace.lints] in Cargo.toml.\n" +
"Run `bun run gen:clippy` and commit the result.",
);
process.exit(1);
}
console.log("bazel/clippy.bazelrc is in sync with Cargo.toml.");
} else if (current === expected) {
console.log("bazel/clippy.bazelrc already up to date.");
} else {
await Bun.write(outputPath, expected);
console.log("Wrote bazel/clippy.bazelrc.");
}
+5 -1
View File
@@ -340,12 +340,16 @@ async function cmdRelease(versionOrBump: string): Promise<void> {
}
console.log(` sentinel: ${sentinelName}\n`);
// 4. Regenerate lockfiles
// 4. Regenerate lockfiles and generated configs
console.log("Regenerating lockfiles...");
await $`rm -f bun.lock`;
await $`bun install`;
await $`cargo generate-lockfile`;
await generateNixBunDeps(nixBunDepsGenerator);
// bazel/clippy.bazelrc mirrors [workspace.lints] in Cargo.toml; regenerate
// it here (like the lockfiles) so the bazel clippy policy can never drift.
// The release_gate CI job runs the matching `--check`.
await $`bun scripts/gen-clippy-bazelrc.ts`;
console.log();
// 5. Update changelogs