From e03931447a62100a8b5818907d88ff9193cc7b0f Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Wed, 18 Feb 2026 10:17:17 -0600 Subject: [PATCH] Add warning when `uv lock` is run with frozen flag When `UV_FROZEN=1` is set or `--frozen` is passed to `uv lock`, the command silently succeeds without updating the lockfile. This can be confusing to users who expect `uv lock` to always update the lockfile. This change adds a warning message when `uv lock` runs in frozen mode, informing the user that the lockfile was not updated. When the frozen mode comes from an environment variable or configuration (rather than an explicit `--frozen` flag), the warning also suggests using `--no-frozen` to override the setting. Closes #12783 https://claude.ai/code/session_01R6PyJQNXRPmox6ePF4aP7j --- crates/uv/src/commands/project/lock.rs | 17 +++++++++ crates/uv/tests/it/lock.rs | 51 ++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index ef73149e03719..55e50d3a23218 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -208,6 +208,22 @@ pub(crate) async fn lock( .await { Ok(lock) => { + if let Some(frozen_source) = frozen { + match frozen_source { + FrozenSource::Cli => { + warn_user!( + "The lockfile at `uv.lock` was only checked for validity, not whether it is up-to-date, because `--frozen` was provided; use `--check` instead" + ); + } + FrozenSource::Env | FrozenSource::Configuration => { + warn_user!( + "The lockfile at `uv.lock` was only checked for validity, not whether it is up-to-date, because {} was provided; use `--no-frozen` or `--check` instead", + MissingLockfileSource::from(frozen_source) + ); + } + } + } + if dry_run.enabled() { // In `--dry-run` mode, show all changes. if let LockResult::Changed(previous, lock) = &lock { @@ -335,6 +351,7 @@ impl<'env> LockOperation<'env> { .read() .await? .ok_or(ProjectError::MissingLockfile(source))?; + // Check if the discovered workspace members match the locked workspace members. if let LockTarget::Workspace(workspace) = target { for package_name in workspace.packages().keys() { diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 59f683b9f0970..22cdd752bc275 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -33446,3 +33446,54 @@ fn lock_tilde_equal_version_u64_max_rejected() -> Result<()> { Ok(()) } + +/// Test that `uv lock --frozen` and `UV_FROZEN=1` show a warning. +/// +/// See: +#[test] +fn lock_frozen_warning() -> Result<()> { + let context = uv_test::test_context!("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["iniconfig==2.0.0"] + "#, + )?; + + // Create the initial lockfile. + uv_snapshot!(context.filters(), context.lock(), @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 2 packages in [TIME] + "); + + // Running `uv lock --frozen` should show a warning. + uv_snapshot!(context.filters(), context.lock().arg("--frozen"), @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: The lockfile at `uv.lock` was only checked for validity, not whether it is up-to-date, because `--frozen` was provided; use `--check` instead + "); + + // Running `uv lock` with `UV_FROZEN=1` should show a warning. + uv_snapshot!(context.filters(), context.lock().env(EnvVars::UV_FROZEN, "1"), @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: The lockfile at `uv.lock` was only checked for validity, not whether it is up-to-date, because `UV_FROZEN=1` was provided; use `--no-frozen` or `--check` instead + "); + + Ok(()) +}