From 7aa23bea1b57f8ad1cb26d555a86befd7aaff447 Mon Sep 17 00:00:00 2001 From: Race Williams Date: Sun, 8 Dec 2024 15:54:09 -0500 Subject: [PATCH 1/8] error triggers w/ wrong package --- compiler-core/src/dependency.rs | 6 +-- compiler-core/src/error.rs | 80 +++++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs index e1e709d7f81..ed5d4032018 100644 --- a/compiler-core/src/dependency.rs +++ b/compiler-core/src/dependency.rs @@ -28,8 +28,8 @@ where { tracing::info!("resolving_versions"); let root_version = Version::new(0, 0, 0); - let requirements = - root_dependencies(dependencies, locked).map_err(Error::dependency_resolution_failed)?; + let requirements = root_dependencies(dependencies, locked) + .map_err(|err| Error::dependency_resolution_failed(err, locked))?; // Creating a map of all the required packages that have exact versions specified let exact_deps = &requirements @@ -55,7 +55,7 @@ where root_name.as_str().into(), root_version, ) - .map_err(Error::dependency_resolution_failed)? + .map_err(|err| Error::dependency_resolution_failed(err, locked))? .into_iter() .filter(|(name, _)| name.as_str() != root_name.as_str()) .collect(); diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index 4ebc1f71d35..b1a49fc285f 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -17,7 +17,7 @@ use pubgrub::package::Package; use pubgrub::report::DerivationTree; use pubgrub::version::Version; use std::borrow::Cow; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::fmt::{Debug, Display}; use std::io::Write; @@ -238,6 +238,12 @@ file_names.iter().map(|x| x.as_str()).join(", "))] #[error("Dependency tree resolution failed: {0}")] DependencyResolutionFailed(String), + #[error("{error}")] + DependencyResolutionFailedWithLocked { + locked_packages: Vec, + error: String, + }, + #[error("The package {0} is listed in dependencies and dev-dependencies")] DuplicateDependency(EcoString), @@ -378,7 +384,10 @@ impl Error { Self::TarFinish(error.to_string()) } - pub fn dependency_resolution_failed(error: ResolutionError) -> Error { + pub fn dependency_resolution_failed( + error: ResolutionError, + locked: &HashMap, + ) -> Error { fn collect_conflicting_packages<'dt, P: Package, V: Version>( derivation_tree: &'dt DerivationTree, conflicting_packages: &mut HashSet<&'dt P>, @@ -413,13 +422,37 @@ impl Error { let mut conflicting_packages = HashSet::new(); collect_conflicting_packages(&derivation_tree, &mut conflicting_packages); - wrap_format!("Unable to find compatible versions for \ -the version constraints in your gleam.toml. \ -The conflicting packages are: + let conflict_names: Vec = conflicting_packages + .iter() + .map(|pkg| (*pkg).to_string().into()) + .collect(); + + let locked_conflicts: Vec = conflict_names + .iter() + .filter(|name| locked.contains_key(*name)) + .cloned() + .collect(); + + if !locked_conflicts.is_empty() { + Error::DependencyResolutionFailedWithLocked { + error: wrap_format!( + "Unable to find compatible versions due to locked package versions in your gleam.toml.\n\ + Consider unlocking or loosening the version constraints for:\n{}", + locked_conflicts.iter().map(|n| format!("- {n}")).join("\n") + ), + locked_packages: locked_conflicts, + }.to_string() + } else { + Error::DependencyResolutionFailed( + wrap_format!( + "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ + The conflicting packages are:\n{}", + conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n") + ) + ).to_string() + } + -{} -", - conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n")) } ResolutionError::ErrorRetrievingDependencies { @@ -3570,7 +3603,36 @@ The error from the version resolver library was: }] } - Error::GitDependencyUnsupported => vec![Diagnostic { + Error::DependencyResolutionFailedWithLocked { error, locked_packages } => { + let locked_list = locked_packages + .iter() + .map(|pkg| format!("- {pkg}")) + .collect::>() + .join("\n"); + let text = format!( + +"An error occurred while determining what dependency packages and +versions should be downloaded. +The error from the version resolver library was: + + {} + + To resolve this issue, consider loosening or removing the locked versions in your gleam.toml for: + {} + ", + wrap(error), + locked_list, + ); + vec![Diagnostic { + title: "Dependency resolution with a locked package".into(), + text, + hint: Some("Try removing or adjusting the locked version(s) in your gleam.toml and re-run the command.".into()), + location: None, + level: Level::Error, + }] + }, + + Error::GitDependencyUnsupported => vec![Diagnostic { title: "Git dependencies are not currently supported".into(), text: "Please remove all git dependencies from the gleam.toml file".into(), hint: None, From 0c5aa84c9024c690e1e58ca8fff726d1a3f9d09a Mon Sep 17 00:00:00 2001 From: Race Williams Date: Thu, 12 Dec 2024 23:03:30 -0500 Subject: [PATCH 2/8] correctly match on new WithLocked type. initiate CI action when relevant --- compiler-cli/src/dependencies.rs | 40 +++++++++-- compiler-core/src/dependency.rs | 1 + compiler-core/src/error.rs | 111 ++++++++++++++++--------------- 3 files changed, 94 insertions(+), 58 deletions(-) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 5c0b00c7932..616eb4e945b 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -691,18 +691,50 @@ fn resolve_versions( } // Convert provided packages into hex packages for pub-grub resolve - let provided_hex_packages = provided_packages + let provided_hex_packages: HashMap = provided_packages .iter() .map(|(name, package)| (name.clone(), package.to_hex_package(name))) .collect(); - let resolved = dependency::resolve_versions( + let root_requirements_clone = root_requirements.clone(); + let resolved: HashMap = match dependency::resolve_versions( PackageFetcher::boxed(runtime.clone()), - provided_hex_packages, + provided_hex_packages.clone(), config.name.clone(), root_requirements.into_iter(), &locked, - )?; + ) { + Ok(it) => it, + Err( + ref e @ Error::DependencyResolutionFailedWithLocked { + error: _, + ref locked_conflicts, + }, + ) => { + // TODO: provide more error context + let should_try_unlock = cli::confirm( + "\nSome of these dependencies are locked to specific versions. It may +be possible to find a solution if they are unlocked, would you like +to unlock and try again? [y/n]", + )?; + + if should_try_unlock { + unlock_packages(&mut locked, &locked_conflicts, manifest)?; + + dependency::resolve_versions( + PackageFetcher::boxed(runtime.clone()), + provided_hex_packages, + config.name.clone(), + root_requirements_clone.into_iter(), + &locked, + )? + } else { + return Err(e.clone()); + } + } + + Err(err) => return Err(err), + }; // Convert the hex packages and local packages into manifest packages let manifest_packages = runtime.block_on(future::try_join_all( diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs index ed5d4032018..ffe9905844d 100644 --- a/compiler-core/src/dependency.rs +++ b/compiler-core/src/dependency.rs @@ -28,6 +28,7 @@ where { tracing::info!("resolving_versions"); let root_version = Version::new(0, 0, 0); + let requirements = root_dependencies(dependencies, locked) .map_err(|err| Error::dependency_resolution_failed(err, locked))?; diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index b1a49fc285f..61d3afaf3a2 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -238,10 +238,11 @@ file_names.iter().map(|x| x.as_str()).join(", "))] #[error("Dependency tree resolution failed: {0}")] DependencyResolutionFailed(String), - #[error("{error}")] + #[error("Dependency tree resolution failed due to locked packages: {error}")] DependencyResolutionFailedWithLocked { - locked_packages: Vec, error: String, + // a vec of the names of locked dependencies responsible for the failure + locked_conflicts: Vec, }, #[error("The package {0} is listed in dependencies and dev-dependencies")] @@ -415,7 +416,7 @@ impl Error { } } - Self::DependencyResolutionFailed(match error { + match error { ResolutionError::NoSolution(mut derivation_tree) => { derivation_tree.collapse_no_versions(); @@ -423,70 +424,79 @@ impl Error { collect_conflicting_packages(&derivation_tree, &mut conflicting_packages); let conflict_names: Vec = conflicting_packages - .iter() - .map(|pkg| (*pkg).to_string().into()) - .collect(); + .iter() + .map(|pkg| (*pkg).to_string().into()) + .collect(); - let locked_conflicts: Vec = conflict_names - .iter() - .filter(|name| locked.contains_key(*name)) - .cloned() - .collect(); + let locked_conflicts: Vec = conflict_names + .iter() + .filter(|name| locked.contains_key(*name)) + .cloned() + .collect(); if !locked_conflicts.is_empty() { - Error::DependencyResolutionFailedWithLocked { - error: wrap_format!( - "Unable to find compatible versions due to locked package versions in your gleam.toml.\n\ - Consider unlocking or loosening the version constraints for:\n{}", - locked_conflicts.iter().map(|n| format!("- {n}")).join("\n") - ), - locked_packages: locked_conflicts, - }.to_string() - } else { - Error::DependencyResolutionFailed( - wrap_format!( - "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ - The conflicting packages are:\n{}", - conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n") + Error::DependencyResolutionFailedWithLocked { + error: wrap_format!( + "Unable to find compatible versions due to package versions locked by manifest.toml.\n\ + Consider unlocking the responsible locked package(s) :\n{}", + locked_conflicts.iter().map(|s| format!("- {s}")).join("\n") + ), + locked_conflicts, + } + } else { + Error::DependencyResolutionFailed( + wrap_format!( + "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ + The conflicting packages are:\n{}", + conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n") + ) ) - ).to_string() - } - - - } + } + } // end [`ResolutionError::NoSolution`] arm ResolutionError::ErrorRetrievingDependencies { package, version, source, - } => format!( + } => { + let msg = format!( "An error occurred while trying to retrieve dependencies of {package}@{version}: {source}", - ), + ); + Error::DependencyResolutionFailed(msg) + } ResolutionError::DependencyOnTheEmptySet { package, version, dependent, - } => format!( - "{package}@{version} has an impossible dependency on {dependent}", - ), + } => { + let msg = + format!("{package}@{version} has an impossible dependency on {dependent}",); + + Error::DependencyResolutionFailed(msg) + } ResolutionError::SelfDependency { package, version } => { - format!("{package}@{version} somehow depends on itself.") + let msg = format!("{package}@{version} somehow depends on itself."); + Error::DependencyResolutionFailed(msg) } ResolutionError::ErrorChoosingPackageVersion(err) => { - format!("Unable to determine package versions: {err}") + let msg = format!("Unable to determine package versions: {err}"); + Error::DependencyResolutionFailed(msg) } ResolutionError::ErrorInShouldCancel(err) => { - format!("Dependency resolution was cancelled. {err}") + let msg = format!("Dependency resolution was cancelled. {err}"); + Error::DependencyResolutionFailed(msg) } - ResolutionError::Failure(err) => format!( - "An unrecoverable error happened while solving dependencies: {err}" - ), - }) + ResolutionError::Failure(err) => { + let msg = + format!("An unrecoverable error happened while solving dependencies: {err}"); + Error::DependencyResolutionFailed(msg) + } + } } pub fn expand_tar(error: E) -> Error @@ -3603,30 +3613,23 @@ The error from the version resolver library was: }] } - Error::DependencyResolutionFailedWithLocked { error, locked_packages } => { - let locked_list = locked_packages - .iter() - .map(|pkg| format!("- {pkg}")) - .collect::>() - .join("\n"); + // locked_conflicts ignored as the version resolver lib builds the message + // enumerating them + Error::DependencyResolutionFailedWithLocked{error, locked_conflicts: _} => { let text = format!( - "An error occurred while determining what dependency packages and versions should be downloaded. The error from the version resolver library was: {} - To resolve this issue, consider loosening or removing the locked versions in your gleam.toml for: - {} ", - wrap(error), - locked_list, + wrap(error) ); vec![Diagnostic { title: "Dependency resolution with a locked package".into(), text, - hint: Some("Try removing or adjusting the locked version(s) in your gleam.toml and re-run the command.".into()), + hint: Some("Try removing locked version(s) in your manifest.toml and re-run the command.".into()), location: None, level: Level::Error, }] From 309f1392999a5f395714e8f035c2430c9e73cbca Mon Sep 17 00:00:00 2001 From: Race Williams Date: Mon, 30 Dec 2024 18:35:12 -0500 Subject: [PATCH 3/8] new error returned in 2nd case, change test --- compiler-cli/src/dependencies.rs | 4 +++- compiler-core/src/dependency.rs | 18 +++++++++++++----- compiler-core/src/error.rs | 18 +++++++++++++++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 616eb4e945b..526f1d16b68 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -715,12 +715,14 @@ fn resolve_versions( let should_try_unlock = cli::confirm( "\nSome of these dependencies are locked to specific versions. It may be possible to find a solution if they are unlocked, would you like -to unlock and try again? [y/n]", +to unlock and try again?", )?; if should_try_unlock { + // unlock pkgs unlock_packages(&mut locked, &locked_conflicts, manifest)?; + // try again dependency::resolve_versions( PackageFetcher::boxed(runtime.clone()), provided_hex_packages, diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs index ffe9905844d..ed65e78c60b 100644 --- a/compiler-core/src/dependency.rs +++ b/compiler-core/src/dependency.rs @@ -130,6 +130,8 @@ where .map_err(|e| ResolutionError::Failure(format!("Failed to parse range {e}")))? .contains(locked_version); if !compatible { + // see [`crate::error::dependency_resolution_failed`] when + // changing this error's text fmt return Err(ResolutionError::Failure(format!( "{name} is specified with the requirement `{range}`, \ but it is locked to {locked_version}, which is incompatible.", @@ -790,11 +792,17 @@ mod tests { .unwrap_err(); match err { - Error::DependencyResolutionFailed(msg) => assert_eq!( - msg, - "An unrecoverable error happened while solving dependencies: gleam_stdlib is specified with the requirement `~> 0.1.0`, but it is locked to 0.2.0, which is incompatible." - ), - _ => panic!("wrong error: {err}"), + Error::DependencyResolutionFailedWithLocked { + error, + locked_conflicts, + } => { + assert_eq!( + error, + format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ + Consider unlocking the responsible locked package(s) :\n{}", locked_conflicts[0]), + ); + } + _ => panic!("wrong error: {err}"), } } diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index 61d3afaf3a2..1ef196659dd 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -492,9 +492,21 @@ impl Error { } ResolutionError::Failure(err) => { - let msg = - format!("An unrecoverable error happened while solving dependencies: {err}"); - Error::DependencyResolutionFailed(msg) + // TODO: something better than looking in err string + let default_msg = format!("Dependency resolution was cancelled. {err}"); + if err.contains(", but it is locked to") { + // first word is package name + match err.split_whitespace().next() { + Some(pkg) => Error::DependencyResolutionFailedWithLocked { + error: format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ + Consider unlocking the responsible locked package(s) :\n{}", pkg), + locked_conflicts: vec![pkg.into()], + }, + None => Error::DependencyResolutionFailed("no pkg".to_string()), + } + } else { + Error::DependencyResolutionFailed(default_msg) + } } } } From 0d6e669b29f44cdbb1a5b43ff04914bbf018e454 Mon Sep 17 00:00:00 2001 From: Race Williams Date: Mon, 30 Dec 2024 19:43:35 -0500 Subject: [PATCH 4/8] indirect test passes, comments --- compiler-core/src/dependency.rs | 62 +++++++++++++++++++++++++++++++-- compiler-core/src/error.rs | 7 ++-- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs index ed65e78c60b..ac9af7119b4 100644 --- a/compiler-core/src/dependency.rs +++ b/compiler-core/src/dependency.rs @@ -794,12 +794,70 @@ mod tests { match err { Error::DependencyResolutionFailedWithLocked { error, - locked_conflicts, + locked_conflicts: _, } => { assert_eq!( error, format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ - Consider unlocking the responsible locked package(s) :\n{}", locked_conflicts[0]), + Consider unlocking the responsible locked package(s) :\n- gleam_stdlib"), + ); + } + _ => panic!("wrong error: {err}"), + } + } + + // These are errors where a locked package version is incompatible with a new package added via gleam add or via a manual gleam.toml update and gleam deps download AND the locked package is not constrained in manifest.toml. + #[test] + fn resolution_locked_version_doesnt_satisfy_requirements_indirect() { + // we're creating a dependency logging v1.4.0 that requires gleam_stdlib v0.40.0 + let mut requirements: HashMap = HashMap::new(); + let _ = requirements.insert( + "gleam_stdlib".to_string(), + Dependency { + requirement: Range::new("~> 0.40.0".to_string()), + optional: false, + app: None, + repository: None, + }, + ); + let mut provided_packages: HashMap = HashMap::new(); + let _ = provided_packages.insert( + "logging".into(), + hexpm::Package { + name: "logging".to_string(), + repository: "test".to_string(), + releases: vec![Release { + version: Version::new(1, 4, 0), + requirements: requirements, + retirement_status: None, + outer_checksum: vec![0], + meta: (), + }], + }, + ); + + // now try and resolve versions with gleam_stdlib v0.20.0 in lock. + let err = resolve_versions( + make_remote(), + provided_packages, + "app".into(), + vec![("logging".into(), Range::new(">= 1.3.0 and < 2.0.0".into()))].into_iter(), + &vec![("gleam_stdlib".into(), Version::new(0, 20, 0))] + .into_iter() + .collect(), + ) + .unwrap_err(); + + // expect failure + match err { + Error::DependencyResolutionFailedWithLocked { + error, + locked_conflicts: _, + } => { + assert_eq!( + error, + format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ + Consider unlocking the responsible locked package(s) :\n- gleam_stdlib"), ); } _ => panic!("wrong error: {err}"), diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index 1ef196659dd..bda9c5a35a4 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -436,7 +436,7 @@ impl Error { if !locked_conflicts.is_empty() { Error::DependencyResolutionFailedWithLocked { - error: wrap_format!( + error: format!( "Unable to find compatible versions due to package versions locked by manifest.toml.\n\ Consider unlocking the responsible locked package(s) :\n{}", locked_conflicts.iter().map(|s| format!("- {s}")).join("\n") @@ -445,7 +445,7 @@ impl Error { } } else { Error::DependencyResolutionFailed( - wrap_format!( + format!( "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ The conflicting packages are:\n{}", conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n") @@ -492,14 +492,13 @@ impl Error { } ResolutionError::Failure(err) => { - // TODO: something better than looking in err string let default_msg = format!("Dependency resolution was cancelled. {err}"); if err.contains(", but it is locked to") { // first word is package name match err.split_whitespace().next() { Some(pkg) => Error::DependencyResolutionFailedWithLocked { error: format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ - Consider unlocking the responsible locked package(s) :\n{}", pkg), + Consider unlocking the responsible locked package(s) :\n- {}", pkg), locked_conflicts: vec![pkg.into()], }, None => Error::DependencyResolutionFailed("no pkg".to_string()), From d40fb24d1b79114426d97725dbee9496b52240f7 Mon Sep 17 00:00:00 2001 From: Race Williams Date: Mon, 30 Dec 2024 19:46:20 -0500 Subject: [PATCH 5/8] lint --- compiler-cli/src/dependencies.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 526f1d16b68..813c83c7152 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -720,7 +720,7 @@ to unlock and try again?", if should_try_unlock { // unlock pkgs - unlock_packages(&mut locked, &locked_conflicts, manifest)?; + unlock_packages(&mut locked, locked_conflicts, manifest)?; // try again dependency::resolve_versions( From 25b98b44719a21e343017add7a4a0b850194a900 Mon Sep 17 00:00:00 2001 From: Race Williams Date: Wed, 1 Jan 2025 19:08:41 -0500 Subject: [PATCH 6/8] add ci check --- compiler-cli/src/dependencies.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 813c83c7152..5371a32569d 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -649,6 +649,23 @@ impl PartialEq for ProvidedPackageSource { } } +// Estimates whether the CLI is ran in a CI environment for use in silencing +// certain CLI dialogues. +fn is_ci_env() -> bool { + let ci_vars = [ + "CI", + "TRAVIS", + "CIRCLECI", + "GITHUB_ACTIONS", + "GITLAB_CI", + "JENKINS_URL", + "TF_BUILD", + "BITBUCKET_COMMIT", + ]; + + ci_vars.iter().any(|var| std::env::var_os(*var).is_some()) +} + fn resolve_versions( runtime: tokio::runtime::Handle, mode: Mode, @@ -711,7 +728,10 @@ fn resolve_versions( ref locked_conflicts, }, ) => { - // TODO: provide more error context + if !is_ci_env() { + return Err(e.clone()); + } + let should_try_unlock = cli::confirm( "\nSome of these dependencies are locked to specific versions. It may be possible to find a solution if they are unlocked, would you like From 654f19c754deb675b708a65d980f9426001722c0 Mon Sep 17 00:00:00 2001 From: Race Williams Date: Thu, 16 Jan 2025 11:44:51 -0500 Subject: [PATCH 7/8] delete unnessesary new error variant, clean old comment, fix mistaken logic --- compiler-cli/src/dependencies.rs | 14 ++-- compiler-core/src/dependency.rs | 42 ++--------- compiler-core/src/error.rs | 122 +++++++++++++------------------ 3 files changed, 62 insertions(+), 116 deletions(-) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 5371a32569d..bf18de76217 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -723,26 +723,22 @@ fn resolve_versions( ) { Ok(it) => it, Err( - ref e @ Error::DependencyResolutionFailedWithLocked { + ref err @ Error::DependencyResolutionFailed { error: _, ref locked_conflicts, }, ) => { if !is_ci_env() { - return Err(e.clone()); + return Err(err.clone()); } - let should_try_unlock = cli::confirm( + if cli::confirm( "\nSome of these dependencies are locked to specific versions. It may be possible to find a solution if they are unlocked, would you like to unlock and try again?", - )?; - - if should_try_unlock { - // unlock pkgs + )? { unlock_packages(&mut locked, locked_conflicts, manifest)?; - // try again dependency::resolve_versions( PackageFetcher::boxed(runtime.clone()), provided_hex_packages, @@ -751,7 +747,7 @@ to unlock and try again?", &locked, )? } else { - return Err(e.clone()); + return Err(err.clone()); } } diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs index ac9af7119b4..57d99b9eba7 100644 --- a/compiler-core/src/dependency.rs +++ b/compiler-core/src/dependency.rs @@ -778,35 +778,6 @@ mod tests { .unwrap_err(); } - #[test] - fn resolution_locked_version_doesnt_satisfy_requirements() { - let err = resolve_versions( - make_remote(), - HashMap::new(), - "app".into(), - vec![("gleam_stdlib".into(), Range::new("~> 0.1.0".into()))].into_iter(), - &vec![("gleam_stdlib".into(), Version::new(0, 2, 0))] - .into_iter() - .collect(), - ) - .unwrap_err(); - - match err { - Error::DependencyResolutionFailedWithLocked { - error, - locked_conflicts: _, - } => { - assert_eq!( - error, - format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ - Consider unlocking the responsible locked package(s) :\n- gleam_stdlib"), - ); - } - _ => panic!("wrong error: {err}"), - } - } - - // These are errors where a locked package version is incompatible with a new package added via gleam add or via a manual gleam.toml update and gleam deps download AND the locked package is not constrained in manifest.toml. #[test] fn resolution_locked_version_doesnt_satisfy_requirements_indirect() { // we're creating a dependency logging v1.4.0 that requires gleam_stdlib v0.40.0 @@ -825,7 +796,7 @@ mod tests { "logging".into(), hexpm::Package { name: "logging".to_string(), - repository: "test".to_string(), + repository: "repository".to_string(), releases: vec![Release { version: Version::new(1, 4, 0), requirements: requirements, @@ -840,7 +811,7 @@ mod tests { let err = resolve_versions( make_remote(), provided_packages, - "app".into(), + "root_name".into(), vec![("logging".into(), Range::new(">= 1.3.0 and < 2.0.0".into()))].into_iter(), &vec![("gleam_stdlib".into(), Version::new(0, 20, 0))] .into_iter() @@ -850,15 +821,16 @@ mod tests { // expect failure match err { - Error::DependencyResolutionFailedWithLocked { + Error::DependencyResolutionFailed { error, - locked_conflicts: _, + locked_conflicts, } => { assert_eq!( error, - format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ - Consider unlocking the responsible locked package(s) :\n- gleam_stdlib"), + format!("Unable to find compatible versions for the version constraints in your gleam.toml.\n\ + The conflicting packages are:\n- gleam_stdlib"), ); + assert_eq!(locked_conflicts, vec!["gleam_stdlib"]) } _ => panic!("wrong error: {err}"), } diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index bda9c5a35a4..edbfe3bfd62 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -235,11 +235,8 @@ file_names.iter().map(|x| x.as_str()).join(", "))] #[error("Failed to create canonical path for package {0}")] DependencyCanonicalizationFailed(String), - #[error("Dependency tree resolution failed: {0}")] - DependencyResolutionFailed(String), - - #[error("Dependency tree resolution failed due to locked packages: {error}")] - DependencyResolutionFailedWithLocked { + #[error("Dependency tree resolution failed: {error}")] + DependencyResolutionFailed { error: String, // a vec of the names of locked dependencies responsible for the failure locked_conflicts: Vec, @@ -434,35 +431,39 @@ impl Error { .cloned() .collect(); - if !locked_conflicts.is_empty() { - Error::DependencyResolutionFailedWithLocked { - error: format!( - "Unable to find compatible versions due to package versions locked by manifest.toml.\n\ - Consider unlocking the responsible locked package(s) :\n{}", - locked_conflicts.iter().map(|s| format!("- {s}")).join("\n") - ), - locked_conflicts, + if locked_conflicts.is_empty() { + Error::DependencyResolutionFailed { + error: format!( + "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ + The conflicting packages are:\n{}", + conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n") + ), + locked_conflicts, } } else { - Error::DependencyResolutionFailed( - format!( - "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ - The conflicting packages are:\n{}", - conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n") - ) - ) + Error::DependencyResolutionFailed { + error: format!( + "Unable to find compatible versions for the version constraints in your gleam.toml.\n\ + The conflicting packages are:\n{}", + locked_conflicts.iter().map(|s| format!("- {s}")).join("\n") + ), + locked_conflicts, + } + } - } // end [`ResolutionError::NoSolution`] arm + + } ResolutionError::ErrorRetrievingDependencies { package, version, source, } => { - let msg = format!( - "An error occurred while trying to retrieve dependencies of {package}@{version}: {source}", - ); - Error::DependencyResolutionFailed(msg) + Error::DependencyResolutionFailed{ + error: format!( + "An error occurred while trying to retrieve dependencies of {package}@{version}: {source}"), + locked_conflicts: vec![], + } } ResolutionError::DependencyOnTheEmptySet { @@ -470,41 +471,37 @@ impl Error { version, dependent, } => { - let msg = - format!("{package}@{version} has an impossible dependency on {dependent}",); - - Error::DependencyResolutionFailed(msg) + Error::DependencyResolutionFailed{ + error: format!("{package}@{version} has an impossible dependency on {dependent}"), + locked_conflicts: vec![], + } } ResolutionError::SelfDependency { package, version } => { - let msg = format!("{package}@{version} somehow depends on itself."); - Error::DependencyResolutionFailed(msg) + Error::DependencyResolutionFailed{ + error: format!("{package}@{version} somehow depends on itself."), + locked_conflicts: vec![], + } } ResolutionError::ErrorChoosingPackageVersion(err) => { - let msg = format!("Unable to determine package versions: {err}"); - Error::DependencyResolutionFailed(msg) + Error::DependencyResolutionFailed{ + error: format!("Unable to determine package versions: {err}"), + locked_conflicts: vec![], + } } ResolutionError::ErrorInShouldCancel(err) => { - let msg = format!("Dependency resolution was cancelled. {err}"); - Error::DependencyResolutionFailed(msg) + Error::DependencyResolutionFailed{ + error: format!("Dependency resolution was cancelled. {err}"), + locked_conflicts: vec![], + } } ResolutionError::Failure(err) => { - let default_msg = format!("Dependency resolution was cancelled. {err}"); - if err.contains(", but it is locked to") { - // first word is package name - match err.split_whitespace().next() { - Some(pkg) => Error::DependencyResolutionFailedWithLocked { - error: format!("Unable to find compatible versions due to package versions locked by manifest.toml.\n\ - Consider unlocking the responsible locked package(s) :\n- {}", pkg), - locked_conflicts: vec![pkg.into()], - }, - None => Error::DependencyResolutionFailed("no pkg".to_string()), - } - } else { - Error::DependencyResolutionFailed(default_msg) + Error::DependencyResolutionFailed{ + error: format!("An unrecoverable error happened while solving dependencies: {err}"), + locked_conflicts: vec![], } } } @@ -3606,7 +3603,9 @@ The error from the parser was: }] } - Error::DependencyResolutionFailed(error) => { + // locked_conflicts ignored as the version resolver lib builds the message + // enumerating them + Error::DependencyResolutionFailed{error, locked_conflicts: _} => { let text = format!( "An error occurred while determining what dependency packages and versions should be downloaded. @@ -3622,31 +3621,10 @@ The error from the version resolver library was: location: None, level: Level::Error, }] - } - - // locked_conflicts ignored as the version resolver lib builds the message - // enumerating them - Error::DependencyResolutionFailedWithLocked{error, locked_conflicts: _} => { - let text = format!( -"An error occurred while determining what dependency packages and -versions should be downloaded. -The error from the version resolver library was: - - {} - - ", - wrap(error) - ); - vec![Diagnostic { - title: "Dependency resolution with a locked package".into(), - text, - hint: Some("Try removing locked version(s) in your manifest.toml and re-run the command.".into()), - location: None, - level: Level::Error, - }] }, - Error::GitDependencyUnsupported => vec![Diagnostic { + + Error::GitDependencyUnsupported => vec![Diagnostic { title: "Git dependencies are not currently supported".into(), text: "Please remove all git dependencies from the gleam.toml file".into(), hint: None, From b27fa5b61c35a2d4e3081ba7002e8ffe5434c771 Mon Sep 17 00:00:00 2001 From: Race Williams Date: Thu, 16 Jan 2025 12:21:16 -0500 Subject: [PATCH 8/8] add back og test, remove stale comments --- compiler-cli/src/dependencies.rs | 3 ++- compiler-core/src/dependency.rs | 26 +++++++++++++++++++++++--- compiler-core/src/error.rs | 3 --- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index bf18de76217..0256e03654b 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -728,7 +728,8 @@ fn resolve_versions( ref locked_conflicts, }, ) => { - if !is_ci_env() { + // Do not ask the user to unlock conflicts in CI or if they don't exist + if is_ci_env() || locked_conflicts.is_empty() { return Err(err.clone()); } diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs index 57d99b9eba7..d24d328454e 100644 --- a/compiler-core/src/dependency.rs +++ b/compiler-core/src/dependency.rs @@ -130,8 +130,6 @@ where .map_err(|e| ResolutionError::Failure(format!("Failed to parse range {e}")))? .contains(locked_version); if !compatible { - // see [`crate::error::dependency_resolution_failed`] when - // changing this error's text fmt return Err(ResolutionError::Failure(format!( "{name} is specified with the requirement `{range}`, \ but it is locked to {locked_version}, which is incompatible.", @@ -779,7 +777,29 @@ mod tests { } #[test] - fn resolution_locked_version_doesnt_satisfy_requirements_indirect() { + fn resolution_locked_version_doesnt_satisfy_requirements() { + let err = resolve_versions( + make_remote(), + HashMap::new(), + "app".into(), + vec![("gleam_stdlib".into(), Range::new("~> 0.1.0".into()))].into_iter(), + &vec![("gleam_stdlib".into(), Version::new(0, 2, 0))] + .into_iter() + .collect(), + ) + .unwrap_err(); + + match err { + Error::DependencyResolutionFailed{error, locked_conflicts: _} => assert_eq!( + error, + "An unrecoverable error happened while solving dependencies: gleam_stdlib is specified with the requirement `~> 0.1.0`, but it is locked to 0.2.0, which is incompatible." + ), + _ => panic!("wrong error: {err}"), + } + } + + #[test] + fn resolution_locked_version_doesnt_satisfy_requirements_locked() { // we're creating a dependency logging v1.4.0 that requires gleam_stdlib v0.40.0 let mut requirements: HashMap = HashMap::new(); let _ = requirements.insert( diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index edbfe3bfd62..9abd5369476 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -238,7 +238,6 @@ file_names.iter().map(|x| x.as_str()).join(", "))] #[error("Dependency tree resolution failed: {error}")] DependencyResolutionFailed { error: String, - // a vec of the names of locked dependencies responsible for the failure locked_conflicts: Vec, }, @@ -3603,8 +3602,6 @@ The error from the parser was: }] } - // locked_conflicts ignored as the version resolver lib builds the message - // enumerating them Error::DependencyResolutionFailed{error, locked_conflicts: _} => { let text = format!( "An error occurred while determining what dependency packages and