Skip to content

Commit 1fab536

Browse files
committed
feat(lock): Print lockfile changes on all commands
1 parent d86e3d8 commit 1fab536

File tree

3 files changed

+185
-10
lines changed

3 files changed

+185
-10
lines changed

src/cargo/ops/cargo_generate_lockfile.rs

Lines changed: 176 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,21 @@ pub struct UpdateOptions<'a> {
2525

2626
pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
2727
let mut registry = PackageRegistry::new(ws.gctx())?;
28+
let previous_resolve = None;
2829
let mut resolve = ops::resolve_with_previous(
2930
&mut registry,
3031
ws,
3132
&CliFeatures::new_all(true),
3233
HasDevUnits::Yes,
33-
None,
34+
previous_resolve,
3435
None,
3536
&[],
3637
true,
3738
)?;
38-
ops::write_pkg_lockfile(ws, &mut resolve)?;
39+
let changed = ops::write_pkg_lockfile(ws, &mut resolve)?;
40+
if changed {
41+
print_lockfile_changes(ws.gctx(), previous_resolve, &resolve, &mut registry)?;
42+
}
3943
Ok(())
4044
}
4145

@@ -171,7 +175,157 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
171175
Ok(())
172176
}
173177

174-
fn print_lockfile_updates(
178+
pub fn print_lockfile_changes(
179+
gctx: &GlobalContext,
180+
previous_resolve: Option<&Resolve>,
181+
resolve: &Resolve,
182+
registry: &mut PackageRegistry<'_>,
183+
) -> CargoResult<()> {
184+
if let Some(previous_resolve) = previous_resolve {
185+
print_lockfile_sync(gctx, previous_resolve, resolve, registry)
186+
} else {
187+
print_lockfile_generation(gctx, resolve, registry)
188+
}
189+
}
190+
191+
fn print_lockfile_generation(
192+
gctx: &GlobalContext,
193+
resolve: &Resolve,
194+
registry: &mut PackageRegistry<'_>,
195+
) -> CargoResult<()> {
196+
let mut shell = gctx.shell();
197+
198+
let num_pkgs = resolve.iter().count();
199+
if num_pkgs == 1 {
200+
// just ourself, nothing worth reporting
201+
return Ok(());
202+
}
203+
shell.status("Locking", format!("{num_pkgs} packages"))?;
204+
205+
for diff in PackageDiff::new(&resolve) {
206+
fn format_latest(version: semver::Version) -> String {
207+
let warn = style::WARN;
208+
format!(" {warn}(latest: v{version}){warn:#}")
209+
}
210+
let possibilities = if let Some(query) = diff.alternatives_query() {
211+
loop {
212+
match registry.query_vec(&query, QueryKind::Exact) {
213+
std::task::Poll::Ready(res) => {
214+
break res?;
215+
}
216+
std::task::Poll::Pending => registry.block_until_ready()?,
217+
}
218+
}
219+
} else {
220+
vec![]
221+
};
222+
223+
for package in diff.added.iter() {
224+
let latest = if !possibilities.is_empty() {
225+
possibilities
226+
.iter()
227+
.map(|s| s.as_summary())
228+
.filter(|s| is_latest(s.version(), package.version()))
229+
.map(|s| s.version().clone())
230+
.max()
231+
.map(format_latest)
232+
} else {
233+
None
234+
};
235+
236+
if let Some(latest) = latest {
237+
shell.status_with_color("Adding", format!("{package}{latest}"), &style::NOTE)?;
238+
}
239+
}
240+
}
241+
242+
Ok(())
243+
}
244+
245+
fn print_lockfile_sync(
246+
gctx: &GlobalContext,
247+
previous_resolve: &Resolve,
248+
resolve: &Resolve,
249+
registry: &mut PackageRegistry<'_>,
250+
) -> CargoResult<()> {
251+
let mut shell = gctx.shell();
252+
253+
shell.status("Locking", "packages")?;
254+
255+
for diff in PackageDiff::diff(&previous_resolve, &resolve) {
256+
fn format_latest(version: semver::Version) -> String {
257+
let warn = style::WARN;
258+
format!(" {warn}(latest: v{version}){warn:#}")
259+
}
260+
let possibilities = if let Some(query) = diff.alternatives_query() {
261+
loop {
262+
match registry.query_vec(&query, QueryKind::Exact) {
263+
std::task::Poll::Ready(res) => {
264+
break res?;
265+
}
266+
std::task::Poll::Pending => registry.block_until_ready()?,
267+
}
268+
}
269+
} else {
270+
vec![]
271+
};
272+
273+
if let Some((removed, added)) = diff.change() {
274+
let latest = if !possibilities.is_empty() {
275+
possibilities
276+
.iter()
277+
.map(|s| s.as_summary())
278+
.filter(|s| is_latest(s.version(), added.version()))
279+
.map(|s| s.version().clone())
280+
.max()
281+
.map(format_latest)
282+
} else {
283+
None
284+
}
285+
.unwrap_or_default();
286+
287+
let msg = if removed.source_id().is_git() {
288+
format!(
289+
"{removed} -> #{}",
290+
&added.source_id().precise_git_fragment().unwrap()[..8],
291+
)
292+
} else {
293+
format!("{removed} -> v{}{latest}", added.version())
294+
};
295+
296+
// If versions differ only in build metadata, we call it an "update"
297+
// regardless of whether the build metadata has gone up or down.
298+
// This metadata is often stuff like git commit hashes, which are
299+
// not meaningfully ordered.
300+
if removed.version().cmp_precedence(added.version()) == Ordering::Greater {
301+
shell.status_with_color("Downgrading", msg, &style::WARN)?;
302+
} else {
303+
shell.status_with_color("Updating", msg, &style::GOOD)?;
304+
}
305+
} else {
306+
for package in diff.added.iter() {
307+
let latest = if !possibilities.is_empty() {
308+
possibilities
309+
.iter()
310+
.map(|s| s.as_summary())
311+
.filter(|s| is_latest(s.version(), package.version()))
312+
.map(|s| s.version().clone())
313+
.max()
314+
.map(format_latest)
315+
} else {
316+
None
317+
}
318+
.unwrap_or_default();
319+
320+
shell.status_with_color("Adding", format!("{package}{latest}"), &style::NOTE)?;
321+
}
322+
}
323+
}
324+
325+
Ok(())
326+
}
327+
328+
pub fn print_lockfile_updates(
175329
gctx: &GlobalContext,
176330
previous_resolve: &Resolve,
177331
resolve: &Resolve,
@@ -324,11 +478,21 @@ pub struct PackageDiff {
324478
}
325479

326480
impl PackageDiff {
327-
pub fn diff(previous_resolve: &Resolve, resolve: &Resolve) -> Vec<Self> {
328-
fn key(dep: PackageId) -> (&'static str, SourceId) {
329-
(dep.name().as_str(), dep.source_id())
481+
pub fn new(resolve: &Resolve) -> Vec<Self> {
482+
let mut changes = BTreeMap::new();
483+
let empty = Self::default();
484+
for dep in resolve.iter() {
485+
changes
486+
.entry(Self::key(dep))
487+
.or_insert_with(|| empty.clone())
488+
.added
489+
.push(dep);
330490
}
331491

492+
changes.into_iter().map(|(_, v)| v).collect()
493+
}
494+
495+
pub fn diff(previous_resolve: &Resolve, resolve: &Resolve) -> Vec<Self> {
332496
fn vec_subset(a: &[PackageId], b: &[PackageId]) -> Vec<PackageId> {
333497
a.iter().filter(|a| !contains_id(b, a)).cloned().collect()
334498
}
@@ -369,14 +533,14 @@ impl PackageDiff {
369533
let empty = Self::default();
370534
for dep in previous_resolve.iter() {
371535
changes
372-
.entry(key(dep))
536+
.entry(Self::key(dep))
373537
.or_insert_with(|| empty.clone())
374538
.removed
375539
.push(dep);
376540
}
377541
for dep in resolve.iter() {
378542
changes
379-
.entry(key(dep))
543+
.entry(Self::key(dep))
380544
.or_insert_with(|| empty.clone())
381545
.added
382546
.push(dep);
@@ -402,6 +566,10 @@ impl PackageDiff {
402566
changes.into_iter().map(|(_, v)| v).collect()
403567
}
404568

569+
fn key(dep: PackageId) -> (&'static str, SourceId) {
570+
(dep.name().as_str(), dep.source_id())
571+
}
572+
405573
/// Guess if a package upgraded/downgraded
406574
///
407575
/// All `PackageDiff` knows is that entries were added/removed within [`Resolve`].

src/cargo/ops/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub use self::cargo_compile::{CompileFilter, FilterRule, LibRule, Packages};
88
pub use self::cargo_doc::{doc, DocOptions, OutputFormat};
99
pub use self::cargo_fetch::{fetch, FetchOptions};
1010
pub use self::cargo_generate_lockfile::generate_lockfile;
11+
pub use self::cargo_generate_lockfile::print_lockfile_changes;
12+
pub use self::cargo_generate_lockfile::print_lockfile_updates;
1113
pub use self::cargo_generate_lockfile::update_lockfile;
1214
pub use self::cargo_generate_lockfile::PackageDiff;
1315
pub use self::cargo_generate_lockfile::UpdateOptions;

src/cargo/ops/resolve.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,13 @@ fn resolve_with_registry<'gctx>(
249249
true,
250250
)?;
251251

252-
if !ws.is_ephemeral() && ws.require_optional_deps() {
253-
ops::write_pkg_lockfile(ws, &mut resolve)?;
252+
let print = if !ws.is_ephemeral() && ws.require_optional_deps() {
253+
ops::write_pkg_lockfile(ws, &mut resolve)?
254+
} else {
255+
false
256+
};
257+
if print {
258+
ops::print_lockfile_changes(ws.gctx(), prev.as_ref(), &resolve, registry)?;
254259
}
255260
Ok(resolve)
256261
}

0 commit comments

Comments
 (0)