Skip to content

Commit 081f21b

Browse files
committed
Add '--to-lockfile' flag to cargo-upgrade
When this flag is present, all dependencies in 'Cargo.toml' are upgraded to the locked version as recorded in 'Cargo.lock'.
1 parent 8f8028e commit 081f21b

File tree

7 files changed

+1787
-14
lines changed

7 files changed

+1787
-14
lines changed

src/bin/add/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use cargo_edit::{find, update_registry_index, Dependency, Manifest};
1919
use std::io::Write;
2020
use std::process;
2121
use structopt::StructOpt;
22-
use toml_edit::Item as TomlItem;
2322
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
23+
use toml_edit::Item as TomlItem;
2424

2525
mod args;
2626

src/bin/upgrade/main.rs

+72-9
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ Dev, build, and all target dependencies will also be upgraded. Only dependencies
5353
supported. Git/path dependencies will be ignored.
5454
5555
All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
56-
be supplied in the presence of a virtual manifest."
56+
be supplied in the presence of a virtual manifest.
57+
58+
If the '--to-lockfile' flag is supplied, all dependencies will be upraged to the currently locked
59+
version as recorded in the local lock file. The local lock file must be present and up to date if
60+
this flag is passed."
5761
)]
5862
Upgrade(Args),
5963
}
@@ -82,6 +86,10 @@ struct Args {
8286
/// Run without accessing the network
8387
#[structopt(long = "offline")]
8488
pub offline: bool,
89+
90+
/// Upgrade all packages to the version in the lockfile.
91+
#[structopt(long = "to-lockfile", conflicts_with = "dependency")]
92+
pub to_lockfile: bool,
8593
}
8694

8795
/// A collection of manifests.
@@ -140,9 +148,7 @@ impl Manifests {
140148
Ok(Manifests(vec![(manifest, package.to_owned())]))
141149
}
142150

143-
/// Get the the combined set of dependencies to upgrade. If the user has specified
144-
/// per-dependency desired versions, extract those here.
145-
fn get_dependencies(&self, only_update: Vec<String>) -> Result<DesiredUpgrades> {
151+
fn get_all_requested_dependencies(&self) -> Vec<cargo_metadata::Dependency> {
146152
/// Helper function to check whether a `cargo_metadata::Dependency` is a version dependency.
147153
fn is_version_dep(dependency: &cargo_metadata::Dependency) -> bool {
148154
match dependency.source {
@@ -153,13 +159,69 @@ impl Manifests {
153159
}
154160
}
155161

162+
self.0
163+
.iter()
164+
.flat_map(|&(_, ref package)| package.dependencies.clone())
165+
.filter(is_version_dep)
166+
.collect()
167+
}
168+
169+
fn get_locked_dependencies(&self) -> Result<Vec<cargo_metadata::Package>> {
170+
Ok(self
171+
.0
172+
.iter()
173+
.map(|(manifest, _package)| {
174+
let mut cmd = cargo_metadata::MetadataCommand::new();
175+
cmd.manifest_path(manifest.path.clone());
176+
cmd.other_options(vec!["--locked".to_string()]);
177+
178+
let result = cmd
179+
.exec()
180+
.map_err(|e| Error::from(e.compat()).chain_err(|| "Invalid manifest"))?;
181+
182+
Ok(result
183+
.packages
184+
.into_iter()
185+
.filter(|p| p.source.is_some())
186+
.collect::<Vec<_>>())
187+
})
188+
.collect::<Result<Vec<_>>>()?
189+
.into_iter()
190+
.flatten()
191+
.collect())
192+
}
193+
194+
/// Get the the combined set of dependencies to upgrade. If the user has specified
195+
/// per-dependency desired versions, extract those here.
196+
fn get_dependencies(
197+
&self,
198+
only_update: Vec<String>,
199+
to_lockfile: bool,
200+
) -> Result<DesiredUpgrades> {
201+
if to_lockfile {
202+
let locked = self.get_locked_dependencies()?;
203+
return Ok(DesiredUpgrades(
204+
self.get_all_requested_dependencies()
205+
.into_iter()
206+
.filter_map(|d| {
207+
for p in &locked {
208+
// The requested depenency may be present in the lock file with different versions,
209+
// but only one will be semver-compatible with requested version.
210+
if d.name == p.name && d.req.matches(&p.version) {
211+
return Some((d.name, Some(p.version.to_string())));
212+
}
213+
}
214+
return None;
215+
})
216+
.collect(),
217+
));
218+
}
219+
156220
Ok(DesiredUpgrades(if only_update.is_empty() {
157221
// User hasn't asked for any specific dependencies to be upgraded, so upgrade all the
158222
// dependencies.
159-
self.0
160-
.iter()
161-
.flat_map(|&(_, ref package)| package.dependencies.clone())
162-
.filter(is_version_dep)
223+
self.get_all_requested_dependencies()
224+
.into_iter()
163225
.map(|dependency| (dependency.name, None))
164226
.collect()
165227
} else {
@@ -255,6 +317,7 @@ fn process(args: Args) -> Result<()> {
255317
all,
256318
allow_prerelease,
257319
dry_run,
320+
to_lockfile,
258321
..
259322
} = args;
260323

@@ -268,7 +331,7 @@ fn process(args: Args) -> Result<()> {
268331
Manifests::get_local_one(&manifest_path)
269332
}?;
270333

271-
let existing_dependencies = manifests.get_dependencies(dependency)?;
334+
let existing_dependencies = manifests.get_dependencies(dependency, to_lockfile)?;
272335

273336
let upgraded_dependencies =
274337
existing_dependencies.get_upgraded(allow_prerelease, &find(&manifest_path)?)?;

src/manifest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ impl str::FromStr for Manifest {
355355
#[derive(Debug)]
356356
pub struct LocalManifest {
357357
/// Path to the manifest
358-
path: PathBuf,
358+
pub path: PathBuf,
359359
/// Manifest contents
360360
manifest: Manifest,
361361
}

tests/cargo-add.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,9 @@ fn adds_sorted_dependencies() {
11011101

11021102
// and all the dependencies in the output get sorted
11031103
let toml = get_toml(&manifest);
1104-
assert_eq!(toml.to_string(), r#"[package]
1104+
assert_eq!(
1105+
toml.to_string(),
1106+
r#"[package]
11051107
name = "cargo-list-test-fixture"
11061108
version = "0.0.0"
11071109
@@ -1112,4 +1114,3 @@ toml_edit = "0.1.5"
11121114
"#
11131115
);
11141116
}
1115-

tests/cargo-upgrade.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[macro_use]
22
extern crate pretty_assertions;
33

4-
use std::fs;
4+
use std::{fs, path::Path};
55

66
mod utils;
77
use crate::utils::{clone_out_test, execute_command, get_command_path, get_toml};
@@ -309,6 +309,23 @@ For more information try --help ",
309309
.unwrap();
310310
}
311311

312+
// Verify that an upgraded Cargo.toml matches what we expect.
313+
#[test]
314+
fn upgrade_to_lockfile() {
315+
let (tmpdir, manifest) = clone_out_test("tests/fixtures/upgrade/Cargo.toml.source");
316+
fs::copy(
317+
Path::new("tests/fixtures/upgrade/Cargo.lock"),
318+
tmpdir.path().join("Cargo.lock"),
319+
)
320+
.unwrap_or_else(|err| panic!("could not copy test lock file: {}", err));;
321+
execute_command(&["upgrade", "--to-lockfile"], &manifest);
322+
323+
let upgraded = get_toml(&manifest);
324+
let target = get_toml("tests/fixtures/upgrade/Cargo.toml.lockfile_target");
325+
326+
assert_eq!(target.to_string(), upgraded.to_string());
327+
}
328+
312329
#[test]
313330
#[cfg(feature = "test-external-apis")]
314331
fn upgrade_prints_messages() {

0 commit comments

Comments
 (0)