From 174c859cf93acf5529052397bf2446f5ebfd237c Mon Sep 17 00:00:00 2001 From: Liang Date: Sat, 24 Oct 2026 08:59:40 +0800 Subject: [PATCH 1/2] fix(env): show warnings in doctor summary --- .../snapshots.toml | 9 +++ .../snapshots/command_env_doctor_warnings.md | 42 +++++++++++ .../vp_global_cli/src/commands/env/doctor.rs | 70 +++++++++---------- rfcs/env-command.md | 4 ++ 4 files changed, 89 insertions(+), 36 deletions(-) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml new file mode 100644 index 0000000000..ef2ab693a0 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml @@ -0,0 +1,9 @@ +[[case]] +name = "command_env_doctor_warnings" +vp = "global" +skip-platforms = ["windows"] +comment = "Warnings describe setups that still work, so doctor names them in the summary and exits with 0." +steps = [ + { argv = ["vpt", "write-file", "$HOME/.profile", ". \"$HOME/.vite-plus/env\""], snapshot = false, comment = "source the env file so the output has no platform-specific IDE guidance" }, + { argv = ["vp", "env", "doctor", "node"], envs = [["VP_NODE_VERSION", "20.18.0"]] }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md new file mode 100644 index 0000000000..205195df28 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md @@ -0,0 +1,42 @@ +# command_env_doctor_warnings + +Warnings describe setups that still work, so doctor names them in the summary and exits with 0. + +## `vpt write-file $HOME/.profile '. "$HOME/.vite-plus/env"'` + +source the env file so the output has no platform-specific IDE guidance + + +## `VP_NODE_VERSION=20.18.0 vp env doctor node` + +``` +VITE+ - The Unified Toolchain for the Web + +Installation + ✓ Bin dir ~/.vite-plus/bin + ✓ Data dir ~/.vite-plus + ✓ Cache dir ~/.vite-plus/cache (not created yet) + ✓ Config dir ~/.vite-plus + ✓ State dir ~/.vite-plus + ✓ Shims node + +Configuration + ✓ Node.js managed mode + ✓ IDE integration env sourced in ~/.profile + ⚠ Session override VP_NODE_VERSION=20.18.0 + note: Overrides all file-based resolution. + note: Run 'vp env use --unset' to remove. + +PATH + ✓ vp ~/.vite-plus/bin/vp + ✓ Shim dir ~/.vite-plus/bin + ✓ node ~/.vite-plus/bin/node (vp shim) + +Node.js Resolution + Directory + Source VP_NODE_VERSION + Version 20.18.0 + ✓ Node binary installed + +⚠ Some warnings found. +``` diff --git a/crates/vp_global_cli/src/commands/env/doctor.rs b/crates/vp_global_cli/src/commands/env/doctor.rs index 54f5e4c9d6..9c60a3604c 100644 --- a/crates/vp_global_cli/src/commands/env/doctor.rs +++ b/crates/vp_global_cli/src/commands/env/doctor.rs @@ -1,6 +1,9 @@ //! Doctor command implementation for environment diagnostics. -use std::process::ExitStatus; +use std::{ + process::ExitStatus, + sync::atomic::{AtomicBool, Ordering}, +}; use console::style; use vp_pm_cli::{package_manager_bin_path, package_manager_install_dir}; @@ -42,6 +45,9 @@ const KNOWN_VERSION_MANAGERS: &[(&str, &str)] = &[ /// Column width for left-side keys in aligned output const KEY_WIDTH: usize = 18; +// Warnings come from many checks that otherwise return nothing, so they are recorded as they print. +static HAS_WARNINGS: AtomicBool = AtomicBool::new(false); + /// Print a section header (bold, with blank line before). fn print_section(name: &str) { vp_shared::output::print_stdout_line(format_args!("")); @@ -62,6 +68,12 @@ fn print_check(status: &str, key: &str, value: &str) { } } +/// Print an aligned key-value line with a warning indicator, and record it for the summary. +fn print_warning(key: &str, value: &str) { + HAS_WARNINGS.store(true, Ordering::Relaxed); + print_check(&style(output::WARN_SIGN).yellow().to_string(), key, value); +} + fn print_package_manager_mode(key: &str, mode: ShimMode) { let mode = match mode { ShimMode::Managed => "managed mode", @@ -168,6 +180,13 @@ pub async fn execute(cwd: AbsolutePathBuf, scope: Option) -> Result Option<(config::Config, Option { @@ -482,8 +498,7 @@ fn check_env_sourcing() -> EnvSourcingStatus { // Second: check all shell profiles (interactive terminal sessions) if let Some(file) = check_profile_files(&env_path, ALL_SHELL_PROFILES) { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), + print_warning( "IDE integration", &format!( "{} {}", @@ -502,8 +517,7 @@ fn check_session_override() { if let Ok(version) = std::env::var(config::VERSION_ENV_VAR) { let version = version.trim(); if !version.is_empty() { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), + print_warning( "Session override", &style(format!("{}={version}", env_vars::VP_NODE_VERSION)).yellow().to_string(), ); @@ -514,8 +528,7 @@ fn check_session_override() { // Also check session version file if let Some(version) = config::read_session_version_sync() { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), + print_warning( "Session override (file)", &style(format!("{}={version}", config::SESSION_VERSION_FILE)).yellow().to_string(), ); @@ -601,8 +614,7 @@ async fn check_path(scope: EnvScope) -> bool { &format!("{display} (system)"), ); } else { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), + print_warning( tool, &format!("{} {}", style(&display).yellow(), style("(not vp shim)").dim()), ); @@ -732,8 +744,7 @@ fn print_ide_setup_guidance(env_dir: &vt_path::AbsolutePath) { }; print_section("IDE Setup"); - print_check( - &style(output::WARN_SIGN).yellow().to_string(), + print_warning( "", &style("GUI applications may not see shell PATH changes.").yellow().to_string(), ); @@ -828,11 +839,7 @@ async fn check_current_resolution( &system_node.as_path().display().to_string(), ); } else { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), - "System Node.js", - &style("not found in PATH").yellow().to_string(), - ); + print_warning("System Node.js", &style("not found in PATH").yellow().to_string()); print_hint("Install Node.js or run 'vp env on' to use managed Node.js."); } return (true, None); @@ -861,11 +868,7 @@ async fn check_current_resolution( if tokio::fs::try_exists(&binary_path).await.unwrap_or(false) { print_check(&style(output::CHECK).green().to_string(), "Node binary", "installed"); } else { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), - "Node binary", - &style("not installed").yellow().to_string(), - ); + print_warning("Node binary", &style("not installed").yellow().to_string()); print_hint("Version will be downloaded on first use."); } (true, Some(resolution)) @@ -963,11 +966,7 @@ async fn check_dev_engines( print_section("devEngines"); for finding in findings { if finding.warn { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), - finding.key, - &style(&finding.message).yellow().to_string(), - ); + print_warning(finding.key, &style(&finding.message).yellow().to_string()); } else { print_check(" ", finding.key, &finding.message); } @@ -1295,8 +1294,7 @@ fn check_conflicts() { if !conflicts.is_empty() { print_section("Conflicts"); for manager in &conflicts { - print_check( - &style(output::WARN_SIGN).yellow().to_string(), + print_warning( manager, &style(format!( "detected ({} is set)", diff --git a/rfcs/env-command.md b/rfcs/env-command.md index d6b399c3ef..8fdf2d5695 100644 --- a/rfcs/env-command.md +++ b/rfcs/env-command.md @@ -949,8 +949,12 @@ Configuration Written by 'vp env use'. Run 'vp env use --unset' to remove. ... + +⚠ Some warnings found. ``` +Warnings describe setups that still work, so they keep the exit code at 0. Errors take precedence: when both appear, the summary is `✗ Some issues found.` and the exit code is 1. + **Doctor Output with System-First Mode:** ```bash From 58d758093e851499f430a0252d69675b84de0f52 Mon Sep 17 00:00:00 2001 From: Liang Date: Sat, 24 Oct 2026 09:53:15 +0800 Subject: [PATCH 2/2] test(env): avoid seeded runtime in doctor warnings snapshot --- .../fixtures/command_env_doctor_warnings/snapshots.toml | 1 + .../snapshots/command_env_doctor_warnings.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml index ef2ab693a0..322ce501bf 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots.toml @@ -2,6 +2,7 @@ name = "command_env_doctor_warnings" vp = "global" skip-platforms = ["windows"] +seed-runtime = false comment = "Warnings describe setups that still work, so doctor names them in the summary and exits with 0." steps = [ { argv = ["vpt", "write-file", "$HOME/.profile", ". \"$HOME/.vite-plus/env\""], snapshot = false, comment = "source the env file so the output has no platform-specific IDE guidance" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md index 205195df28..f779fe438f 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_doctor_warnings/snapshots/command_env_doctor_warnings.md @@ -36,7 +36,8 @@ Node.js Resolution Directory Source VP_NODE_VERSION Version 20.18.0 - ✓ Node binary installed + ⚠ Node binary not installed + note: Version will be downloaded on first use. ⚠ Some warnings found. ```