Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions adds-on/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ SPECIAL COMMANDS:
→ real uv completions + plugins injected
shells: bash zsh fish nushell
uv __complete → list discovered plugins (used by shell completions)
uv --version, -V → show wrapper version
uv --wrapper-version → show this wrapper's version
(--version / -V pass through to the real uv)
uv --help, -h → show this message

ENVIRONMENT:
Expand All @@ -43,7 +44,12 @@ fn main() {
Some("-h") | Some("--help") => {
print_help();
}
Some("--version") | Some("-V") => {
// Report the wrapper's own version under a dedicated flag. We must NOT
// intercept `--version`/`-V`: tools like pipx and uv's own bootstrap run
// `uv --version` and parse the second whitespace token as a semver — a
// banner like "uv (plugin wrapper) 2.6.0" breaks them ("Unrecognized uv
// version '(plugin'"). Those flags fall through to the real uv below.
Some("--wrapper-version") => {
println!("uv (plugin wrapper) {}", env!("CARGO_PKG_VERSION"));
}
// Dynamic plugin discovery — called by shell completion at tab-press time
Expand Down
34 changes: 29 additions & 5 deletions adds-on/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,47 @@ fn help_short_flag() {

// ── version ─────────────────────────────────────────────────────

/// The wrapper exposes its own version under a dedicated flag.
#[test]
fn version_long_flag() {
let out = wrapper(&["--version"]);
fn wrapper_version_flag() {
let out = wrapper(&["--wrapper-version"]);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("uv (plugin wrapper)"),
"expected version line, got: {stdout}"
"expected wrapper version line, got: {stdout}"
);
}

/// `--version` must pass through to the real uv unchanged so version-parsing
/// tools (pipx, uv's own bootstrap) see uv's exact output, not our banner.
#[test]
fn version_short_flag() {
fn version_long_flag_passes_through() {
let out = wrapper(&["--version"]);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("plugin wrapper"),
"wrapper banner leaked into --version output: {stdout}"
);
// Real uv prints `uv <semver> ...`; the second token must parse as a version.
let second = stdout.split_whitespace().nth(1).unwrap_or("");
assert!(
second.split('.').next().map(|s| s.chars().all(|c| c.is_ascii_digit())).unwrap_or(false),
"expected `uv <semver>`, got: {stdout}"
);
}

/// `-V` must also pass through to the real uv.
#[test]
fn version_short_flag_passes_through() {
let out = wrapper(&["-V"]);
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains("uv (plugin wrapper)"));
assert!(
!stdout.contains("plugin wrapper"),
"wrapper banner leaked into -V output: {stdout}"
);
}

// ── __complete ──────────────────────────────────────────────────
Expand Down
Loading