Repository: taiki-e/cargo-hack (language: rust)

Issue:
Using --version-range in projects that do not contain Cargo.lock is annoying
Currently, cargo-hack runs `cargo metadata` before the command is run for the specified version, so the format of `Cargo.lock` is based on the cargo version in which you ran `cargo metadata` (if `Cargo.lock` does not exist). 

Specifically, if cargo-hack is run on cargo 1.38+ and --version-range received a version smaller than 1.38 and `Cargo.lock` does not exist, it will fail to compile because it is incompatible with new lock file formats.
https://github.com/taiki-e/auto_enums/runs/1506269414?check_suite_focus=true

This should be workaround by first generating the lock file using an older cargo, but it should be available without such a hack.


Discussion / hints:

Fix patch:
diff --git a/scripts/check-minimal-versions.sh b/scripts/check-minimal-versions.sh
index ad852157..6220f32b 100755
--- a/scripts/check-minimal-versions.sh
+++ b/scripts/check-minimal-versions.sh
@@ -7,7 +7,7 @@
 #
 # Note:
 # - This script modifies Cargo.toml and Cargo.lock while running
-# - This script exits with 1 if there are any unstaged changes
+# - This script exits with 1 if there are any unstaged changes on Cargo.toml
 # - This script requires nightly toolchain and cargo-hack
 #
 # Refs: https://github.com/rust-lang/cargo/issues/5657
@@ -47,11 +47,12 @@ if [[ "${1:-}" =~ ^(check|test)$ ]]; then
   shift
 fi
 
-# This script modifies Cargo.toml and Cargo.lock, so make sure there are no
-# unstaged changes.
-git diff --exit-code
+# This script modifies Cargo.toml, so make sure there are no unstaged changes
+# on Cargo.toml.
+# shellcheck disable=SC2046
+git diff --exit-code $(git ls-files '*Cargo.toml')
 # Restore original Cargo.toml and Cargo.lock on exit.
-trap 'git checkout .' EXIT
+trap 'git checkout $(git ls-files ''*Cargo.toml'')' EXIT
 
 if [[ "${subcmd}" == "check" ]]; then
   # Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
diff --git a/src/main.rs b/src/main.rs
index 8d25faac..0c7fac8a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -68,8 +68,29 @@ fn exec_on_workspace(cx: &Context<'_>) -> Result<()> {
         if cx.verbose {
             line.display_manifest_path();
         }
+        {
+            // First, generate the lockfile using the oldest cargo specified.
+            // https://github.com/taiki-e/cargo-hack/issues/105
+            let toolchain = &range[0];
+            rustup::install_toolchain(toolchain)?;
+            let mut line = line.clone();
+            line.leading_arg(toolchain);
+            line.args(&["generate-lockfile"]);
+            if let Some(pid) = cx.current_package() {
+                let package = cx.packages(pid);
+                line.arg("--manifest-path");
+                line.arg(
+                    package
+                        .manifest_path
+                        .strip_prefix(&cx.current_dir)
+                        .unwrap_or(&package.manifest_path),
+                );
+            }
+            line.exec_with_output()?;
+        }
+
         range.iter().try_for_each(|toolchain| {
-            rustup::install_toolchain(&toolchain[1..])?;
+            rustup::install_toolchain(toolchain)?;
             let mut line = line.clone();
             line.leading_arg(toolchain);
             line.with_args(cx);
diff --git a/src/rustup.rs b/src/rustup.rs
index 093daea4..2e020ef3 100644
--- a/src/rustup.rs
+++ b/src/rustup.rs
@@ -66,7 +66,10 @@ pub(crate) fn version_range(range: &str, step: Option<&str>) -> Result<Vec<Strin
     Ok(versions)
 }
 
-pub(crate) fn install_toolchain(toolchain: &str) -> Result<()> {
+pub(crate) fn install_toolchain(mut toolchain: &str) -> Result<()> {
+    if toolchain.starts_with('+') {
+        toolchain = &toolchain[1..];
+    }
     // In Github Actions and Azure Pipelines, --no-self-update is necessary
     // because the windows environment cannot self-update rustup.exe.
     rustup()


Test patch: