Respect --exclude-newer in uv tool list --outdated (#18861)
## Summary Closes https://github.com/astral-sh/uv/issues/18819.
This commit is contained in:
@@ -5805,6 +5805,19 @@ pub struct ToolListArgs {
|
||||
#[arg(long, overrides_with("outdated"), hide = true)]
|
||||
pub no_outdated: bool,
|
||||
|
||||
/// Limit candidate packages to those that were uploaded prior to the given date.
|
||||
///
|
||||
/// Accepts RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`), local dates in the same format
|
||||
/// (e.g., `2006-12-02`) resolved based on your system's configured time zone, a "friendly"
|
||||
/// duration (e.g., `24 hours`, `1 week`, `30 days`), or an ISO 8601 duration (e.g., `PT24H`,
|
||||
/// `P7D`, `P30D`).
|
||||
///
|
||||
/// Durations do not respect semantics of the local time zone and are always resolved to a fixed
|
||||
/// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
|
||||
/// Calendar units such as months and years are not allowed.
|
||||
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
|
||||
pub exclude_newer: Option<ExcludeNewerValue>,
|
||||
|
||||
// Hide unused global Python options.
|
||||
#[arg(long, hide = true)]
|
||||
pub python_preference: Option<PythonPreference>,
|
||||
|
||||
@@ -15,7 +15,7 @@ use uv_distribution_types::{IndexCapabilities, RequiresPython};
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_python::LenientImplementationName;
|
||||
use uv_settings::ResolverInstallerOptions;
|
||||
use uv_settings::{Combine, ResolverInstallerOptions};
|
||||
use uv_tool::InstalledTools;
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
@@ -34,6 +34,8 @@ pub(crate) async fn list(
|
||||
show_extras: bool,
|
||||
show_python: bool,
|
||||
outdated: bool,
|
||||
args: ResolverInstallerOptions,
|
||||
filesystem: ResolverInstallerOptions,
|
||||
client_builder: BaseClientBuilder<'_>,
|
||||
concurrency: Concurrency,
|
||||
cache: &Cache,
|
||||
@@ -124,10 +126,12 @@ pub(crate) async fn list(
|
||||
.map(|(name, tool, tool_env, _version)| {
|
||||
let client_builder = client_builder.clone();
|
||||
let download_concurrency = download_concurrency.clone();
|
||||
let args = args.clone();
|
||||
let filesystem = filesystem.clone();
|
||||
async move {
|
||||
let capabilities = IndexCapabilities::default();
|
||||
let settings = ResolverInstallerSettings::from(ResolverInstallerOptions::from(
|
||||
tool.options().clone(),
|
||||
let settings = ResolverInstallerSettings::from(args.combine(
|
||||
ResolverInstallerOptions::from(tool.options().clone()).combine(filesystem),
|
||||
));
|
||||
let interpreter = tool_env.environment().interpreter();
|
||||
|
||||
|
||||
@@ -1627,6 +1627,8 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||
args.show_extras,
|
||||
args.show_python,
|
||||
args.outdated,
|
||||
args.args,
|
||||
args.filesystem,
|
||||
client_builder.subcommand(vec!["tool".to_owned(), "list".to_owned()]),
|
||||
globals.concurrency,
|
||||
&cache,
|
||||
|
||||
@@ -1111,12 +1111,13 @@ pub(crate) struct ToolListSettings {
|
||||
pub(crate) show_extras: bool,
|
||||
pub(crate) show_python: bool,
|
||||
pub(crate) outdated: bool,
|
||||
pub(crate) args: ResolverInstallerOptions,
|
||||
pub(crate) filesystem: ResolverInstallerOptions,
|
||||
}
|
||||
|
||||
impl ToolListSettings {
|
||||
/// Resolve the [`ToolListSettings`] from the CLI and filesystem configuration.
|
||||
#[expect(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: ToolListArgs, _filesystem: Option<FilesystemOptions>) -> Self {
|
||||
pub(crate) fn resolve(args: ToolListArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let ToolListArgs {
|
||||
show_paths,
|
||||
show_version_specifiers,
|
||||
@@ -1125,10 +1126,17 @@ impl ToolListSettings {
|
||||
show_python,
|
||||
outdated,
|
||||
no_outdated,
|
||||
exclude_newer,
|
||||
python_preference: _,
|
||||
no_python_downloads: _,
|
||||
} = args;
|
||||
|
||||
let filesystem = filesystem.map(FilesystemOptions::into_options);
|
||||
let filesystem = ResolverInstallerOptions {
|
||||
exclude_newer: filesystem.and_then(|options| options.top_level.exclude_newer),
|
||||
..ResolverInstallerOptions::default()
|
||||
};
|
||||
|
||||
Self {
|
||||
show_paths,
|
||||
show_version_specifiers,
|
||||
@@ -1136,6 +1144,11 @@ impl ToolListSettings {
|
||||
show_extras,
|
||||
show_python,
|
||||
outdated: flag(outdated, no_outdated, "outdated").unwrap_or(false),
|
||||
args: ResolverInstallerOptions {
|
||||
exclude_newer,
|
||||
..ResolverInstallerOptions::default()
|
||||
},
|
||||
filesystem,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,37 @@ fn tool_list_outdated_respects_exclude_newer() {
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_list_outdated_cli_exclude_newer() {
|
||||
let context = uv_test::test_context!("3.12").with_filtered_exe_suffix();
|
||||
let tool_dir = context.temp_dir.child("tools");
|
||||
let bin_dir = context.temp_dir.child("bin");
|
||||
|
||||
// Install an older version of `black`.
|
||||
context
|
||||
.tool_install()
|
||||
.arg("black==24.2.0")
|
||||
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// `--exclude-newer` should filter out releases newer than the cutoff when determining the
|
||||
// latest available tool version.
|
||||
uv_snapshot!(context.filters(), context.tool_list()
|
||||
.arg("--outdated")
|
||||
.arg("--exclude-newer")
|
||||
.arg("2024-03-01T00:00:00Z")
|
||||
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_list_missing_receipt() {
|
||||
let context = uv_test::test_context!("3.12").with_filtered_exe_suffix();
|
||||
|
||||
Reference in New Issue
Block a user