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
4 changes: 2 additions & 2 deletions crates/uv-distribution-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ pub enum VersionOrUrlRef<'a, T: Pep508Url = VerbatimUrl> {
Url(&'a T),
}

impl<T: Pep508Url> VersionOrUrlRef<'_, T> {
impl<'a, T: Pep508Url> VersionOrUrlRef<'a, T> {
/// If it is a URL, return its value.
pub fn url(&self) -> Option<&T> {
pub fn url(&self) -> Option<&'a T> {
match self {
Self::Version(_) => None,
Self::Url(url) => Some(url),
Expand Down
7 changes: 7 additions & 0 deletions crates/uv/src/commands/pip/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,13 @@ impl ChangedDist {
},
}
}

pub(crate) fn version(&self) -> Option<&Version> {
match self {
Self::Local(dist) => Some(dist.installed_version().version()),
Self::Remote(dist) => dist.version(),
}
}
}

/// A summary of the changes made to the environment during an installation.
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ pub(crate) async fn remove(
)
.await
{
Ok(()) => {}
Ok(_) => {}
Err(ProjectError::Operation(err)) => {
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
.report(err)
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
)
.await
{
Ok(()) => {}
Ok(_) => {}
Err(ProjectError::Operation(err)) => {
return diagnostics::OperationDiagnostic::native_tls(
client_builder.is_native_tls(),
Expand Down Expand Up @@ -867,7 +867,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
)
.await
{
Ok(()) => {}
Ok(_) => {}
Err(ProjectError::Operation(err)) => {
return diagnostics::OperationDiagnostic::native_tls(
client_builder.is_native_tls(),
Expand Down
123 changes: 97 additions & 26 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use uv_configuration::{
use uv_dispatch::BuildDispatch;
use uv_distribution::LoweredExtraBuildDependencies;
use uv_distribution_types::{
DirectorySourceDist, Dist, Index, Requirement, Resolution, ResolvedDist, SourceDist,
DirectorySourceDist, Dist, Index, Name, Requirement, Resolution, ResolvedDist, SourceDist,
};
use uv_fs::{PortablePathBuf, Simplified};
use uv_installer::{InstallationStrategy, SitePackages};
Expand All @@ -37,16 +37,16 @@ use uv_workspace::pyproject::Source;
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache};

use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
use crate::commands::pip::operations::Modifications;
use crate::commands::pip::operations::{ChangedDist, Changelog, Modifications};
use crate::commands::pip::resolution_markers;
use crate::commands::pip::{operations, resolution_tags};
use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::lock::{LockMode, LockOperation, LockResult};
use crate::commands::project::lock_target::LockTarget;
use crate::commands::project::{
PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment, UniversalState,
default_dependency_groups, detect_conflicts, script_extra_build_requires, script_specification,
update_environment,
EnvironmentUpdate, PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment,
UniversalState, default_dependency_groups, detect_conflicts, script_extra_build_requires,
script_specification, update_environment,
};
use crate::commands::{ExitStatus, diagnostics};
use crate::printer::Printer;
Expand Down Expand Up @@ -212,6 +212,7 @@ pub(crate) async fn sync(
environment: EnvironmentReport::from(&environment),
action: SyncAction::from(&environment),
target: TargetName::from(&target),
changes: PackageChangesReport::default(),
};

// Show the intermediate results if relevant
Expand Down Expand Up @@ -292,14 +293,17 @@ pub(crate) async fn sync(
)
.await
{
Ok(..) => {
Ok(EnvironmentUpdate { changelog, .. }) => {
// Generate a report for the script without a lockfile
let report = Report {
schema: SchemaReport::default(),
target: TargetName::from(&target),
project: None,
script: Some(ScriptReport::from(script)),
sync: sync_report,
sync: SyncReport {
changes: PackageChangesReport::from_changelog(&changelog),
..sync_report
},
lock: None,
dry_run: dry_run.enabled(),
};
Expand Down Expand Up @@ -387,27 +391,13 @@ pub(crate) async fn sync(
writeln!(printer.stderr(), "{message}")?;
}

let report = Report {
schema: SchemaReport::default(),
target: TargetName::from(&target),
project: target.project().map(ProjectReport::from),
script: target.script().map(ScriptReport::from),
sync: sync_report,
lock: Some(lock_report),
dry_run: dry_run.enabled(),
};

if let Some(output) = report.format(output_format) {
writeln!(printer.stdout_important(), "{output}")?;
}

// Identify the installation target.
let sync_target = identify_installation_target(&target, outcome.lock(), all_packages, &package);

let state = state.fork();

// Perform the sync operation.
match do_sync(
let changelog = match do_sync(
sync_target,
&environment,
&extras,
Expand All @@ -430,13 +420,30 @@ pub(crate) async fn sync(
)
.await
{
Ok(()) => {}
Ok(changelog) => changelog,
Err(ProjectError::Operation(err)) => {
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
.report(err)
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
}
Err(err) => return Err(err.into()),
};

let report = Report {
schema: SchemaReport::default(),
target: TargetName::from(&target),
project: target.project().map(ProjectReport::from),
script: target.script().map(ScriptReport::from),
sync: SyncReport {
changes: PackageChangesReport::from_changelog(&changelog),
..sync_report
},
lock: Some(lock_report),
dry_run: dry_run.enabled(),
};

if let Some(output) = report.format(output_format) {
writeln!(printer.stdout_important(), "{output}")?;
}

match outcome {
Expand Down Expand Up @@ -614,7 +621,7 @@ pub(super) async fn do_sync(
dry_run: DryRun,
printer: Printer,
preview: Preview,
) -> Result<(), ProjectError> {
) -> Result<Changelog, ProjectError> {
// Extract the project settings.
let InstallerSettingsRef {
index_locations,
Expand Down Expand Up @@ -825,7 +832,7 @@ pub(super) async fn do_sync(
let site_packages = SitePackages::from_environment(venv)?;

// Sync the environment.
operations::install(
let changelog = operations::install(
&resolution,
site_packages,
InstallationStrategy::Strict,
Expand All @@ -850,7 +857,7 @@ pub(super) async fn do_sync(
)
.await?;

Ok(())
Ok(changelog)
}

/// Filter out any virtual workspace members.
Expand Down Expand Up @@ -1254,6 +1261,9 @@ struct SyncReport {
environment: EnvironmentReport,
/// The action performed during the sync, e.g., what was done to the environment.
action: SyncAction,
/// The packages that changed during the sync.
#[serde(default)]
changes: PackageChangesReport,

// We store these fields so the report can format itself self-contained, but the outer
// [`Report`] is intended to include these in user-facing output
Expand All @@ -1276,6 +1286,7 @@ impl SyncReport {
let Self {
environment,
action,
changes: _,
dry_run,
target,
} = self;
Expand All @@ -1294,6 +1305,66 @@ impl SyncReport {
}
}

/// A summary of all package changes performed during sync.
#[derive(Serialize, Debug, Clone, Default)]
struct PackageChangesReport(Vec<PackageChangeReport>);

impl PackageChangesReport {
fn from_changelog(changelog: &Changelog) -> Self {
let mut changes: Vec<_> =
changelog
.uninstalled
.iter()
.map(|dist| PackageChangeReport::from_dist(dist, PackageChangeAction::Uninstalled))
.chain(changelog.installed.iter().map(|dist| {
PackageChangeReport::from_dist(dist, PackageChangeAction::Installed)
}))
.chain(changelog.reinstalled.iter().map(|dist| {
PackageChangeReport::from_dist(dist, PackageChangeAction::Reinstalled)
}))
.collect();

changes.sort_by(|a, b| {
a.name
.cmp(&b.name)
.then_with(|| a.action.cmp(&b.action))
.then_with(|| a.version.cmp(&b.version))
});
Self(changes)
}
}

/// A summary of a single package change performed during sync.
#[derive(Serialize, Debug, Clone)]
struct PackageChangeReport {
Comment thread
EliteTK marked this conversation as resolved.
/// The normalized package name.
name: PackageName,
/// The resolved version of the package.
#[serde(skip_serializing_if = "Option::is_none")]
version: Option<uv_pep440::Version>,
/// The action that was taken for the package.
action: PackageChangeAction,
}

impl PackageChangeReport {
fn from_dist(dist: &ChangedDist, action: PackageChangeAction) -> Self {
Self {
name: dist.name().clone(),
version: dist.version().cloned(),
action,
}
}
}

/// The action taken on an individual package during sync.
#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
enum PackageChangeAction {
Uninstalled,
Installed,
Reinstalled,
}

/// The report for a lock operation.
#[derive(Debug, Serialize)]
struct LockReport {
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ async fn lock_and_sync(
)
.await
{
Ok(()) => {}
Ok(_) => {}
Err(ProjectError::Operation(err)) => {
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
.report(err)
Expand Down
Loading