Skip to content

Commit 33256b1

Browse files
committed
vendor: make warning hygiene deterministic and auditable
1 parent 8d60043 commit 33256b1

6 files changed

Lines changed: 183 additions & 1 deletion

File tree

docs/dependency-vendoring.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ What this does:
114114
Run after each vendoring step:
115115

116116
```bash
117+
f vendor-trims
117118
/Users/nikiv/code/rise/scripts/vendor-control.sh verify --project /Users/nikiv/code/flow
119+
python3 ./scripts/vendor/rough_edges_audit.py --project . --strict-warnings
118120
cargo check -q
119121
scripts/vendor/sync-all.sh --important --dry-run
120122
```
@@ -128,6 +130,10 @@ scripts/vendor/sync-all.sh --important --dry-run
128130
- patch path matches lock materialized path,
129131
- manifest version matches lock version.
130132

133+
`vendor-rough-audit --strict-warnings` additionally enforces warning-hygiene
134+
regressions for known vendored crate hot spots (`crossterm`, `portable-pty`,
135+
`x25519-dalek`, `ratatui`) so release builds stay quiet.
136+
131137
## Provenance and Hardening
132138

133139
`inhouse` now records provenance fields in crate manifests:

docs/vendor-optimization-loop.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ while keeping Cargo correctness and upstream sync reliability.
1212
## Commands
1313

1414
```bash
15+
f vendor-trims
1516
f vendor-rough-audit
1617
f vendor-offenders
1718
f vendor-bench-iter -- --mode incremental --samples 3
@@ -39,6 +40,8 @@ f vendor-optimize-loop -- --strict
3940
- provenance fields in manifests (`history_head`, `upstream_repository`),
4041
- stale code index detection (`.vendor/typesense/sources.json` freshness),
4142
- extra drift artifacts (`lib/vendor/*` or patch entries not in lock).
43+
- warning-hygiene regressions in vendored crates that would reintroduce noisy
44+
release-build warnings.
4245

4346
`vendor-offenders` (`scripts/vendor/offenders.sh`) shows:
4447

flow.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ name = "vendor-code-search-sources"
268268
command = "python3 ./scripts/vendor/typesense_code_index.py --project . search --collection sources $@"
269269
description = "Search source inventory metadata (crate/version/path/upstream)"
270270

271+
[[tasks]]
272+
name = "vendor-trims"
273+
command = "bash ./scripts/vendor/apply-trims.sh $@"
274+
description = "Apply deterministic trim + warning-hygiene patches to vendored crates"
275+
271276
[[tasks]]
272277
name = "vendor-rough-audit"
273278
command = "python3 ./scripts/vendor/rough_edges_audit.py --project . $@"

scripts/vendor/rough_edges_audit.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,89 @@ def latest_mtime(paths: list[Path]) -> float:
8686
return latest
8787

8888

89+
def check_warning_hygiene(project: Path) -> list[Finding]:
90+
findings: list[Finding] = []
91+
92+
checks: list[tuple[str, str]] = [
93+
(
94+
"lib/vendor/crossterm/src/lib.rs",
95+
'cfg(all(winapi, not(feature = "winapi")))',
96+
),
97+
(
98+
"lib/vendor/crossterm/src/lib.rs",
99+
'cfg(all(crossterm_winapi, not(feature = "crossterm_winapi")))',
100+
),
101+
(
102+
"lib/vendor/crossterm/src/terminal/sys/unix.rs",
103+
"map(|file| (FileDesc::Owned(file.into())))",
104+
),
105+
(
106+
"lib/vendor/portable-pty/src/unix.rs",
107+
'feature = "cargo-clippy"',
108+
),
109+
(
110+
"lib/vendor/x25519-dalek/src/lib.rs",
111+
'cfg_attr(feature = "bench", feature(test))',
112+
),
113+
(
114+
"lib/vendor/ratatui/src/terminal/terminal.rs",
115+
"pub fn get_frame(&mut self) -> Frame {",
116+
),
117+
(
118+
"lib/vendor/ratatui/src/terminal/terminal.rs",
119+
"pub fn draw<F>(&mut self, render_callback: F) -> io::Result<CompletedFrame>",
120+
),
121+
(
122+
"lib/vendor/ratatui/src/terminal/terminal.rs",
123+
"pub fn try_draw<F, E>(&mut self, render_callback: F) -> io::Result<CompletedFrame>",
124+
),
125+
(
126+
"lib/vendor/ratatui/src/text/line.rs",
127+
"pub fn iter(&self) -> std::slice::Iter<Span<'a>> {",
128+
),
129+
(
130+
"lib/vendor/ratatui/src/text/line.rs",
131+
"pub fn iter_mut(&mut self) -> std::slice::IterMut<Span<'a>> {",
132+
),
133+
(
134+
"lib/vendor/ratatui/src/text/text.rs",
135+
"pub fn iter(&self) -> std::slice::Iter<Line<'a>> {",
136+
),
137+
(
138+
"lib/vendor/ratatui/src/text/text.rs",
139+
"pub fn iter_mut(&mut self) -> std::slice::IterMut<Line<'a>> {",
140+
),
141+
(
142+
"lib/vendor/ratatui/src/text/text.rs",
143+
"fn to_text(&self) -> Text {",
144+
),
145+
(
146+
"lib/vendor/ratatui/src/widgets/block.rs",
147+
") -> impl DoubleEndedIterator<Item = &Line> {",
148+
),
149+
]
150+
151+
for rel_path, needle in checks:
152+
path = project / rel_path
153+
if not path.is_file():
154+
continue
155+
try:
156+
content = path.read_text(encoding="utf-8")
157+
except Exception:
158+
continue
159+
if needle in content:
160+
findings.append(
161+
Finding(
162+
"warn",
163+
"warning_hygiene_regression",
164+
f"{rel_path}: found stale warning pattern `{needle}`",
165+
"run scripts/vendor/apply-trims.sh (or hydrate) to re-apply warning hygiene patches",
166+
)
167+
)
168+
169+
return findings
170+
171+
89172
def build_report(project: Path) -> tuple[dict[str, Any], list[Finding]]:
90173
findings: list[Finding] = []
91174
metrics: dict[str, Any] = {
@@ -96,6 +179,7 @@ def build_report(project: Path) -> tuple[dict[str, Any], list[Finding]]:
96179
"direct_dependencies": 0,
97180
"direct_non_vendored_dependencies": 0,
98181
"direct_non_vendored_list": [],
182+
"warning_hygiene_regressions": 0,
99183
}
100184

101185
vendor_lock_path = project / "vendor.lock.toml"
@@ -401,6 +485,10 @@ def build_report(project: Path) -> tuple[dict[str, Any], list[Finding]]:
401485
)
402486
)
403487

488+
warning_hygiene_findings = check_warning_hygiene(project)
489+
metrics["warning_hygiene_regressions"] = len(warning_hygiene_findings)
490+
findings.extend(warning_hygiene_findings)
491+
404492
return metrics, findings
405493

406494

@@ -411,6 +499,7 @@ def print_text(metrics: dict[str, Any], findings: list[Finding]) -> None:
411499
print(f"vendor patch entries: {metrics['vendor_patch_entries']}")
412500
print(f"direct deps: {metrics['direct_dependencies']}")
413501
print(f"direct deps not yet vendored: {metrics['direct_non_vendored_dependencies']}")
502+
print(f"warning hygiene regressions: {metrics['warning_hygiene_regressions']}")
414503
if metrics["direct_non_vendored_list"]:
415504
preview = ", ".join(metrics["direct_non_vendored_list"][:12])
416505
suffix = " ..." if len(metrics["direct_non_vendored_list"]) > 12 else ""

scripts/vendor/trim-hooks.sh

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,76 @@ apply_ratatui_trims() {
5151
"$root/RELEASE.md" \
5252
"$root/SECURITY.md" \
5353
"$root/BREAKING-CHANGES.md"
54+
55+
# Rust 1.90+ warns on elided lifetime name mismatches in these signatures.
56+
local terminal_file="$root/src/terminal/terminal.rs"
57+
local text_line_file="$root/src/text/line.rs"
58+
local text_text_file="$root/src/text/text.rs"
59+
local widgets_block_file="$root/src/widgets/block.rs"
60+
61+
[[ -f "$terminal_file" ]] && perl -0777 -i -pe '
62+
s/pub fn get_frame\(&mut self\) -> Frame \{/pub fn get_frame(&mut self) -> Frame<'\''_> {/g;
63+
s/pub fn draw<F>\(&mut self, render_callback: F\) -> io::Result<CompletedFrame>/pub fn draw<F>(&mut self, render_callback: F) -> io::Result<CompletedFrame<'\''_>>/g;
64+
s/pub fn try_draw<F, E>\(&mut self, render_callback: F\) -> io::Result<CompletedFrame>/pub fn try_draw<F, E>(&mut self, render_callback: F) -> io::Result<CompletedFrame<'\''_>>/g;
65+
' "$terminal_file"
66+
67+
[[ -f "$text_line_file" ]] && perl -0777 -i -pe '
68+
s/pub fn iter\(&self\) -> std::slice::Iter<Span<'\''a>>/pub fn iter(&self) -> std::slice::Iter<'\''_, Span<'\''a>>/g;
69+
s/pub fn iter_mut\(&mut self\) -> std::slice::IterMut<Span<'\''a>>/pub fn iter_mut(&mut self) -> std::slice::IterMut<'\''_, Span<'\''a>>/g;
70+
' "$text_line_file"
71+
72+
[[ -f "$text_text_file" ]] && perl -0777 -i -pe '
73+
s/pub fn iter\(&self\) -> std::slice::Iter<Line<'\''a>>/pub fn iter(&self) -> std::slice::Iter<'\''_, Line<'\''a>>/g;
74+
s/pub fn iter_mut\(&mut self\) -> std::slice::IterMut<Line<'\''a>>/pub fn iter_mut(&mut self) -> std::slice::IterMut<'\''_, Line<'\''a>>/g;
75+
s/fn to_text\(&self\) -> Text \{/fn to_text(&self) -> Text<'\''_> {/g;
76+
' "$text_text_file"
77+
78+
[[ -f "$widgets_block_file" ]] && perl -0777 -i -pe '
79+
s/\) -> impl DoubleEndedIterator<Item = &Line> \{/) -> impl DoubleEndedIterator<Item = &Line<'\''_>> {/g;
80+
' "$widgets_block_file"
81+
}
82+
83+
apply_crossterm_trims() {
84+
local root="lib/vendor/crossterm"
85+
[[ -d "$root" ]] || return 0
86+
87+
local lib_file="$root/src/lib.rs"
88+
local unix_file="$root/src/terminal/sys/unix.rs"
89+
local filter_file="$root/src/event/filter.rs"
90+
91+
[[ -f "$lib_file" ]] && perl -0777 -i -pe '
92+
s/\n#\[cfg\(all\(winapi, not\(feature = "winapi"\)\)\)\]\ncompile_error!\("Compiling on Windows with \\"winapi\\" feature disabled\. Feature \\"winapi\\" should only be disabled when project will never be compiled on Windows\."\);\n//g;
93+
s/\n#\[cfg\(all\(crossterm_winapi, not\(feature = "crossterm_winapi"\)\)\)\]\ncompile_error!\("Compiling on Windows with \\"crossterm_winapi\\" feature disabled\. Feature \\"crossterm_winapi\\" should only be disabled when project will never be compiled on Windows\."\);\n//g;
94+
' "$lib_file"
95+
96+
[[ -f "$unix_file" ]] && perl -0777 -i -pe '
97+
s/File::open\("\/dev\/tty"\)\.map\(\|file\| \(FileDesc::Owned\(file\.into\(\)\)\)\)/File::open("\/dev\/tty").map(|file| FileDesc::Owned(file.into()))/g;
98+
' "$unix_file"
99+
100+
[[ -f "$filter_file" ]] && perl -0777 -i -pe '
101+
if (!/\#\[allow\(dead_code\)\]\s*pub\(crate\) struct InternalEventFilter;/s) {
102+
s/\#\[derive\(Debug, Clone\)\]\s*pub\(crate\) struct InternalEventFilter;/#[derive(Debug, Clone)]\n#[allow(dead_code)]\npub(crate) struct InternalEventFilter;/s;
103+
}
104+
' "$filter_file"
105+
}
106+
107+
apply_portable_pty_trims() {
108+
local file="lib/vendor/portable-pty/src/unix.rs"
109+
[[ -f "$file" ]] || return 0
110+
111+
perl -0777 -i -pe '
112+
s/\n[ \t]*#\[cfg_attr\(feature = "cargo-clippy", allow\(clippy::unnecessary_mut_passed\)\)\]//g;
113+
s/\n[ \t]*#\[cfg_attr\(feature = "cargo-clippy", allow\(clippy::cast_lossless\)\)\]//g;
114+
' "$file"
115+
}
116+
117+
apply_x25519_dalek_trims() {
118+
local file="lib/vendor/x25519-dalek/src/lib.rs"
119+
[[ -f "$file" ]] || return 0
120+
121+
perl -0777 -i -pe '
122+
s/\n#!\[cfg_attr\(feature = "bench", feature\(test\)\)\]//g;
123+
' "$file"
54124
}
55125

56126
apply_vendor_trims() {
@@ -60,12 +130,18 @@ apply_vendor_trims() {
60130
reqwest) apply_reqwest_trims ;;
61131
axum) apply_axum_trims ;;
62132
ratatui) apply_ratatui_trims ;;
63-
*) echo "warning: no trim rules defined for crate '$crate'" ;;
133+
crossterm) apply_crossterm_trims ;;
134+
portable-pty) apply_portable_pty_trims ;;
135+
x25519-dalek) apply_x25519_dalek_trims ;;
136+
*) ;;
64137
esac
65138
return
66139
fi
67140

68141
apply_reqwest_trims
69142
apply_axum_trims
70143
apply_ratatui_trims
144+
apply_crossterm_trims
145+
apply_portable_pty_trims
146+
apply_x25519_dalek_trims
71147
}

scripts/vendor/vendor-repo.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ cmd_import_local() {
300300
ensure_repo_layout "$checkout"
301301
ensure_git_identity "$checkout"
302302

303+
# Keep imported source deterministic with the same trim/hygiene rules used by hydrate.
304+
scripts/vendor/apply-trims.sh
305+
303306
while IFS=$'\t' read -r name repo_path manifest_path materialized_path; do
304307
[[ -n "$name" ]] || continue
305308

0 commit comments

Comments
 (0)