Always show lock updates in uv lock (#5413)

## Summary

Closes https://github.com/astral-sh/uv/issues/5412.
This commit is contained in:
Charlie Marsh 2024-07-24 11:54:18 -04:00 committed by GitHub
parent 3d36b71ab1
commit 22db997240
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 56 additions and 41 deletions

View file

@ -244,7 +244,7 @@ pub(crate) async fn add(
project::sync::do_sync( project::sync::do_sync(
&VirtualProject::Project(project), &VirtualProject::Project(project),
&venv, &venv,
&lock, &lock.lock,
&extras, &extras,
dev, dev,
Modifications::Sufficient, Modifications::Sufficient,

View file

@ -32,6 +32,15 @@ use crate::commands::{pip, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::{ResolverSettings, ResolverSettingsRef}; use crate::settings::{ResolverSettings, ResolverSettingsRef};
/// The result of running a lock operation.
#[derive(Debug, Clone)]
pub(crate) struct LockResult {
/// The previous lock, if any.
pub(crate) previous: Option<Lock>,
/// The updated lock.
pub(crate) lock: Lock,
}
/// Resolve the project requirements into a lockfile. /// Resolve the project requirements into a lockfile.
pub(crate) async fn lock( pub(crate) async fn lock(
locked: bool, locked: bool,
@ -86,7 +95,12 @@ pub(crate) async fn lock(
) )
.await .await
{ {
Ok(_) => Ok(ExitStatus::Success), Ok(lock) => {
if let Some(previous) = lock.previous.as_ref() {
report_upgrades(previous, &lock.lock, printer)?;
}
Ok(ExitStatus::Success)
}
Err(ProjectError::Operation(pip::operations::Error::Resolve( Err(ProjectError::Operation(pip::operations::Error::Resolve(
uv_resolver::ResolveError::NoSolution(err), uv_resolver::ResolveError::NoSolution(err),
))) => { ))) => {
@ -112,12 +126,16 @@ pub(super) async fn do_safe_lock(
native_tls: bool, native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<Lock, ProjectError> { ) -> Result<LockResult, ProjectError> {
if frozen { if frozen {
// Read the existing lockfile, but don't attempt to lock the project. // Read the existing lockfile, but don't attempt to lock the project.
read(workspace) let existing = read(workspace)
.await? .await?
.ok_or_else(|| ProjectError::MissingLockfile) .ok_or_else(|| ProjectError::MissingLockfile)?;
Ok(LockResult {
previous: None,
lock: existing,
})
} else if locked { } else if locked {
// Read the existing lockfile. // Read the existing lockfile.
let existing = read(workspace) let existing = read(workspace)
@ -145,7 +163,10 @@ pub(super) async fn do_safe_lock(
return Err(ProjectError::LockMismatch); return Err(ProjectError::LockMismatch);
} }
Ok(lock) Ok(LockResult {
previous: Some(existing),
lock,
})
} else { } else {
// Read the existing lockfile. // Read the existing lockfile.
let existing = read(workspace).await?; let existing = read(workspace).await?;
@ -166,16 +187,19 @@ pub(super) async fn do_safe_lock(
) )
.await?; .await?;
if !existing.is_some_and(|existing| existing == lock) { if !existing.as_ref().is_some_and(|existing| *existing == lock) {
commit(&lock, workspace).await?; commit(&lock, workspace).await?;
} }
Ok(lock) Ok(LockResult {
previous: existing,
lock,
})
} }
} }
/// Lock the project requirements into a lockfile. /// Lock the project requirements into a lockfile.
pub(super) async fn do_lock( async fn do_lock(
workspace: &Workspace, workspace: &Workspace,
interpreter: &Interpreter, interpreter: &Interpreter,
existing_lock: Option<&Lock>, existing_lock: Option<&Lock>,
@ -506,16 +530,7 @@ pub(super) async fn do_lock(
// Notify the user of any resolution diagnostics. // Notify the user of any resolution diagnostics.
pip::operations::diagnose_resolution(resolution.diagnostics(), printer)?; pip::operations::diagnose_resolution(resolution.diagnostics(), printer)?;
let new_lock = Lock::from_resolution_graph(&resolution)?; Ok(Lock::from_resolution_graph(&resolution)?)
// Notify the user of any dependency updates
if !upgrade.is_none() {
if let Some(existing_lock) = existing_lock {
report_upgrades(existing_lock, &new_lock, printer)?;
}
}
Ok(new_lock)
} }
/// Write the lockfile to disk. /// Write the lockfile to disk.

View file

@ -131,7 +131,7 @@ pub(crate) async fn remove(
project::sync::do_sync( project::sync::do_sync(
&VirtualProject::Project(project), &VirtualProject::Project(project),
&venv, &venv,
&lock, &lock.lock,
&extras, &extras,
dev, dev,
Modifications::Exact, Modifications::Exact,

View file

@ -238,7 +238,7 @@ pub(crate) async fn run(
project::sync::do_sync( project::sync::do_sync(
&project, &project,
&venv, &venv,
&lock, &lock.lock,
&extras, &extras,
dev, dev,
Modifications::Sufficient, Modifications::Sufficient,

View file

@ -95,7 +95,7 @@ pub(crate) async fn sync(
do_sync( do_sync(
&project, &project,
&venv, &venv,
&lock, &lock.lock,
&extras, &extras,
dev, dev,
modifications, modifications,

View file

@ -83,7 +83,7 @@ pub(crate) async fn tree(
// Read packages from the lockfile. // Read packages from the lockfile.
let mut packages: IndexMap<_, Vec<_>> = IndexMap::new(); let mut packages: IndexMap<_, Vec<_>> = IndexMap::new();
for dist in lock.into_distributions() { for dist in lock.lock.into_distributions() {
let name = dist.name().clone(); let name = dist.name().clone();
let metadata = dist.to_metadata(workspace.install_path())?; let metadata = dist.to_metadata(workspace.install_path())?;
packages.entry(name).or_default().push(metadata); packages.entry(name).or_default().push(metadata);

View file

@ -3569,23 +3569,23 @@ fn lock_new_extras() -> Result<()> {
"#, "#,
)?; )?;
deterministic! { context => uv_snapshot!(context.filters(), context.lock().arg("--preview"), @r###"
uv_snapshot!(context.filters(), context.lock().arg("--preview"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
Resolved 7 packages in [TIME] Resolved 7 packages in [TIME]
Added pysocks v1.7.1
"###); "###);
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap(); let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
insta::with_settings!({ insta::with_settings!({
filters => context.filters(), filters => context.filters(),
}, { }, {
assert_snapshot!( assert_snapshot!(
lock, @r###" lock, @r###"
version = 1 version = 1
requires-python = ">=3.12" requires-python = ">=3.12"
exclude-newer = "2024-03-25 00:00:00 UTC" exclude-newer = "2024-03-25 00:00:00 UTC"
@ -3678,9 +3678,8 @@ fn lock_new_extras() -> Result<()> {
{ url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 }, { url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
] ]
"### "###
); );
}); });
}
Ok(()) Ok(())
} }
@ -3863,14 +3862,15 @@ fn lock_resolution_mode() -> Result<()> {
// Locking with `lowest-direct` should ignore the existing lockfile. // Locking with `lowest-direct` should ignore the existing lockfile.
uv_snapshot!(context.filters(), context.lock().arg("--resolution").arg("lowest-direct"), @r###" uv_snapshot!(context.filters(), context.lock().arg("--resolution").arg("lowest-direct"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
warning: `uv lock` is experimental and may change without warning warning: `uv lock` is experimental and may change without warning
Ignoring existing lockfile due to change in resolution mode: `highest` vs. `lowest-direct` Ignoring existing lockfile due to change in resolution mode: `highest` vs. `lowest-direct`
Resolved 4 packages in [TIME] Resolved 4 packages in [TIME]
Updated anyio v4.3.0 -> v3.0.0
"###); "###);
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap(); let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();