diff --git a/crates/pixi_core/src/lock_file/resolve/pypi.rs b/crates/pixi_core/src/lock_file/resolve/pypi.rs index 66f6520e0a..ae1495ef1a 100644 --- a/crates/pixi_core/src/lock_file/resolve/pypi.rs +++ b/crates/pixi_core/src/lock_file/resolve/pypi.rs @@ -18,7 +18,9 @@ use indicatif::ProgressBar; use itertools::{Either, Itertools}; use miette::{Context, IntoDiagnostic}; use pixi_consts::consts; -use pixi_manifest::{EnvironmentName, SystemRequirements, pypi::pypi_options::PypiOptions}; +use pixi_manifest::{ + EnvironmentName, SolveStrategy, SystemRequirements, pypi::pypi_options::PypiOptions, +}; use pixi_pypi_spec::PixiPypiSpec; use pixi_record::PixiRecord; use pixi_reporters::{UvReporter, UvReporterOptions}; @@ -50,7 +52,8 @@ use uv_pypi_types::{Conflicts, HashAlgorithm, HashDigests}; use uv_requirements::LookaheadResolver; use uv_resolver::{ AllowedYanks, DefaultResolverProvider, FlatIndex, InMemoryIndex, Manifest, Options, Preference, - PreferenceError, Preferences, PythonRequirement, ResolveError, Resolver, ResolverEnvironment, + PreferenceError, Preferences, PythonRequirement, ResolutionMode, ResolveError, Resolver, + ResolverEnvironment, }; use uv_types::EmptyInstalledPackages; @@ -281,6 +284,7 @@ pub async fn resolve_pypi( environment_name: Environment<'_>, disallow_install_conda_prefix: bool, exclude_newer: Option>, + solve_strategy: SolveStrategy, ) -> miette::Result<(LockedPypiPackages, Option)> { // Solve python packages pb.set_message("resolving pypi dependencies"); @@ -455,11 +459,18 @@ pub async fn resolve_pypi( &build_options, ); + let resolution_mode = match solve_strategy { + SolveStrategy::Highest => ResolutionMode::Highest, + SolveStrategy::Lowest => ResolutionMode::Lowest, + SolveStrategy::LowestDirect => ResolutionMode::LowestDirect, + }; + // Hi maintainers! For anyone coming here, if you expose any additional `uv` // options, similar to `index_strategy`, make sure to include them in this // struct as well instead of relying on the default. Otherwise there be // panics. let options = Options { + resolution_mode, index_strategy, build_options: build_options.clone(), exclude_newer: exclude_newer.map(to_exclude_newer).unwrap_or_default(), diff --git a/crates/pixi_core/src/lock_file/satisfiability/mod.rs b/crates/pixi_core/src/lock_file/satisfiability/mod.rs index df4c637054..7d1cb37ef7 100644 --- a/crates/pixi_core/src/lock_file/satisfiability/mod.rs +++ b/crates/pixi_core/src/lock_file/satisfiability/mod.rs @@ -488,10 +488,11 @@ pub fn verify_environment_satisfiability( } // Verify solver options - if locked_environment.solve_options().strategy != environment.solve_strategy() { + let expected_solve_strategy = environment.solve_strategy().into(); + if locked_environment.solve_options().strategy != expected_solve_strategy { return Err(EnvironmentUnsat::SolveStrategyMismatch { locked_strategy: locked_environment.solve_options().strategy, - expected_strategy: environment.solve_strategy(), + expected_strategy: expected_solve_strategy, }); } diff --git a/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@mismatch-solve-strategy2.snap b/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@mismatch-solve-strategy2.snap new file mode 100644 index 0000000000..2eca087566 --- /dev/null +++ b/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@mismatch-solve-strategy2.snap @@ -0,0 +1,7 @@ +--- +source: src/lock_file/satisfiability/mod.rs +expression: s +--- +environment 'default' does not satisfy the requirements of the project + Diagnostic severity: error + Caused by: the lock-file was solved with a different strategy (highest) than the one selected (lowest-version) diff --git a/crates/pixi_core/src/lock_file/update.rs b/crates/pixi_core/src/lock_file/update.rs index e7c3bfbf3a..6e63460451 100644 --- a/crates/pixi_core/src/lock_file/update.rs +++ b/crates/pixi_core/src/lock_file/update.rs @@ -1765,7 +1765,7 @@ impl<'p> UpdateContext<'p> { builder.set_options( &environment_name, rattler_lock::SolveOptions { - strategy: grouped_env.solve_strategy(), + strategy: grouped_env.solve_strategy().into(), channel_priority: grouped_env .channel_priority() .unwrap_or_default() @@ -1914,7 +1914,7 @@ async fn spawn_solve_conda_environment_task( // Get solve options let exclude_newer = group.exclude_newer(); - let strategy = group.solve_strategy(); + let strategy = group.solve_strategy().into(); // Get the environment name let group_name = group.name(); @@ -2241,6 +2241,7 @@ async fn spawn_solve_pypi_task<'p>( }; let environment_name = grouped_environment.name().clone(); + let solve_strategy = grouped_environment.solve_strategy(); let pixi_solve_records = &repodata_records.records; let locked_pypi_records = &locked_pypi_packages.records; @@ -2280,6 +2281,7 @@ async fn spawn_solve_pypi_task<'p>( environment, disallow_install_conda_prefix, exclude_newer, + solve_strategy, ) .await .with_context(|| { diff --git a/crates/pixi_core/src/workspace/environment.rs b/crates/pixi_core/src/workspace/environment.rs index 3054434023..b5e37f540a 100644 --- a/crates/pixi_core/src/workspace/environment.rs +++ b/crates/pixi_core/src/workspace/environment.rs @@ -940,4 +940,90 @@ mod tests { Platform::Win32 ); } + + #[test] + pub fn test_solve_strategy() { + let contents = r#" + [project] + name = "foo" + platforms = [] + channels = [] + solve-strategy = "lowest-direct" + + [feature.lowest] + solve-strategy = "lowest" + + [feature.highest] + solve-strategy = "highest" + + [feature.no_strategy] + + [environments] + lowest = ["lowest"] + highest = ["highest"] + combined-declared = ["lowest", "highest"] + combined-undeclared-first = ["no_strategy", "lowest"] + combined-undeclared-last = ["lowest", "no_strategy"] + undeclared-default = ["no_strategy"] + undeclared-no-default = { features = ["no_strategy"], no-default-feature = true } + "#; + + let temp_dir = tempfile::tempdir().unwrap(); + let workspace = Workspace::from_str(&temp_dir.path().join("pixi.toml"), contents).unwrap(); + + assert_eq!( + workspace.default_environment().solve_strategy(), + pixi_manifest::SolveStrategy::LowestDirect + ); + + assert_eq!( + workspace.environment("lowest").unwrap().solve_strategy(), + pixi_manifest::SolveStrategy::Lowest + ); + + assert_eq!( + workspace.environment("highest").unwrap().solve_strategy(), + pixi_manifest::SolveStrategy::Highest + ); + + assert_eq!( + workspace + .environment("combined-declared") + .unwrap() + .solve_strategy(), + pixi_manifest::SolveStrategy::Lowest + ); + + assert_eq!( + workspace + .environment("combined-undeclared-first") + .unwrap() + .solve_strategy(), + pixi_manifest::SolveStrategy::Lowest + ); + + assert_eq!( + workspace + .environment("combined-undeclared-last") + .unwrap() + .solve_strategy(), + pixi_manifest::SolveStrategy::Lowest + ); + + assert_eq!( + workspace + .environment("undeclared-default") + .unwrap() + .solve_strategy(), + pixi_manifest::SolveStrategy::LowestDirect + ); + + assert_eq!( + workspace + .environment("undeclared-no-default") + .unwrap() + .solve_strategy(), + pixi_manifest::SolveStrategy::Highest + ); + } } diff --git a/crates/pixi_manifest/src/feature.rs b/crates/pixi_manifest/src/feature.rs index 6a13964b75..8cb95ec86e 100644 --- a/crates/pixi_manifest/src/feature.rs +++ b/crates/pixi_manifest/src/feature.rs @@ -1,6 +1,7 @@ use crate::{ SpecType, SystemRequirements, WorkspaceTarget, channel::PrioritizedChannel, consts, pypi::pypi_options::PypiOptions, target::Targets, workspace::ChannelPriority, + workspace::SolveStrategy, }; use indexmap::{IndexMap, IndexSet}; use pixi_pypi_spec::{PixiPypiSpec, PypiPackageName}; @@ -137,6 +138,12 @@ pub struct Feature { /// it will be seen as unset and overwritten by a set one. pub channel_priority: Option, + /// Solve strategy specific for this feature. + /// + /// If this value is `None` and there are multiple features, + /// it will be seen as unset and overwritten by a set one. + pub solve_strategy: Option, + /// Additional system requirements pub system_requirements: SystemRequirements, @@ -155,6 +162,7 @@ impl Feature { platforms: None, channels: None, channel_priority: None, + solve_strategy: None, system_requirements: SystemRequirements::default(), pypi_options: None, targets: as Default>::default(), diff --git a/crates/pixi_manifest/src/features_ext.rs b/crates/pixi_manifest/src/features_ext.rs index 458ba4b30e..a7f7cdd957 100644 --- a/crates/pixi_manifest/src/features_ext.rs +++ b/crates/pixi_manifest/src/features_ext.rs @@ -10,8 +10,10 @@ use rattler_conda_types::{ use crate::{ CondaDependencies, PrioritizedChannel, PyPiDependencies, SpecType, SystemRequirements, - has_features_iter::HasFeaturesIter, has_manifest_ref::HasWorkspaceManifest, - pypi::pypi_options::PypiOptions, workspace::ChannelPriority, + has_features_iter::HasFeaturesIter, + has_manifest_ref::HasWorkspaceManifest, + pypi::pypi_options::PypiOptions, + workspace::{ChannelPriority, SolveStrategy}, }; /// ChannelPriorityCombination error, thrown when multiple channel priorities @@ -93,8 +95,16 @@ pub trait FeaturesExt<'source>: HasWorkspaceManifest<'source> + HasFeaturesIter< } /// Returns the strategy for solving packages. - fn solve_strategy(&self) -> rattler_solve::SolveStrategy { - rattler_solve::SolveStrategy::default() + /// + /// The chosen strategy is the first explicitly declared one in a feature + /// as they are provided by the [`HasFeaturesIter::features`] iterator. + /// + /// If no feature declares a strategy, the default value of [`SolveStrategy`] is used. + fn solve_strategy(&self) -> SolveStrategy { + self.features() + .flat_map(|feature| feature.solve_strategy) + .next() + .unwrap_or_default() } /// Returns the platforms that this collection is compatible with. diff --git a/crates/pixi_manifest/src/lib.rs b/crates/pixi_manifest/src/lib.rs index fe18cf9044..a3fb648080 100644 --- a/crates/pixi_manifest/src/lib.rs +++ b/crates/pixi_manifest/src/lib.rs @@ -59,7 +59,7 @@ pub use target::{PackageTarget, TargetSelector, Targets, WorkspaceTarget}; pub use task::{Task, TaskName}; use thiserror::Error; pub use warning::{Warning, WarningWithSource, WithWarnings}; -pub use workspace::{BuildVariantSource, ChannelPriority, Workspace}; +pub use workspace::{BuildVariantSource, ChannelPriority, SolveStrategy, Workspace}; pub use crate::{ environments::Environments, diff --git a/crates/pixi_manifest/src/toml/feature.rs b/crates/pixi_manifest/src/toml/feature.rs index 4612b8f008..8b2ea90fd6 100644 --- a/crates/pixi_manifest/src/toml/feature.rs +++ b/crates/pixi_manifest/src/toml/feature.rs @@ -16,7 +16,7 @@ use crate::{ }, utils::{PixiSpanned, package_map::UniquePackageMap}, warning::Deprecation, - workspace::ChannelPriority, + workspace::{ChannelPriority, SolveStrategy}, }; use pixi_pypi_spec::{PixiPypiSpec, PypiPackageName}; @@ -27,6 +27,7 @@ pub struct TomlFeature { pub channel_priority: Option, pub system_requirements: SystemRequirements, pub target: IndexMap, TomlTarget>, + pub solve_strategy: Option, pub dependencies: Option>, pub host_dependencies: Option>, pub build_dependencies: Option>, @@ -115,6 +116,7 @@ impl TomlFeature { .channels .map(|channels| channels.into_iter().map(|channel| channel.into()).collect()), channel_priority: self.channel_priority, + solve_strategy: self.solve_strategy, system_requirements: self.system_requirements, pypi_options: self.pypi_options, targets: Targets::from_default_and_user_defined(default_target, targets), @@ -133,6 +135,7 @@ impl<'de> toml_span::Deserialize<'de> for TomlFeature { .map(TomlWith::into_inner); let channels = th.optional("channels"); let channel_priority = th.optional("channel-priority"); + let solve_strategy = th.optional("solve-strategy"); let target = th .optional::>("target") .map(TomlIndexMap::into_inner) @@ -192,6 +195,7 @@ impl<'de> toml_span::Deserialize<'de> for TomlFeature { platforms, channels, channel_priority, + solve_strategy, system_requirements, target, dependencies, diff --git a/crates/pixi_manifest/src/toml/manifest.rs b/crates/pixi_manifest/src/toml/manifest.rs index cd6b0e81d2..c1bfc07f1b 100644 --- a/crates/pixi_manifest/src/toml/manifest.rs +++ b/crates/pixi_manifest/src/toml/manifest.rs @@ -198,6 +198,7 @@ impl TomlManifest { channels: None, channel_priority: workspace.value.channel_priority, + solve_strategy: workspace.value.solve_strategy, system_requirements: self .system_requirements diff --git a/crates/pixi_manifest/src/toml/snapshots/pixi_manifest__toml__manifest__test__run_dependencies_in_feature.snap b/crates/pixi_manifest/src/toml/snapshots/pixi_manifest__toml__manifest__test__run_dependencies_in_feature.snap index 98a605cc71..0b4bb324d4 100644 --- a/crates/pixi_manifest/src/toml/snapshots/pixi_manifest__toml__manifest__test__run_dependencies_in_feature.snap +++ b/crates/pixi_manifest/src/toml/snapshots/pixi_manifest__toml__manifest__test__run_dependencies_in_feature.snap @@ -2,8 +2,8 @@ source: crates/pixi_manifest/src/toml/manifest.rs expression: "expect_parse_failure(r#\"\n [workspace]\n channels = []\n platforms = []\n\n [feature.foobar.run-dependencies]\n \"#,)" --- - × Unexpected keys, expected only 'platforms', 'channels', 'channel-priority', 'target', 'dependencies', 'host-dependencies', 'build-dependencies', 'pypi-dependencies', 'activation', 'tasks', - │ 'pypi-options', 'system-requirements' + × Unexpected keys, expected only 'platforms', 'channels', 'channel-priority', 'solve-strategy', 'target', 'dependencies', 'host-dependencies', 'build-dependencies', 'pypi-dependencies', + │ 'activation', 'tasks', 'pypi-options', 'system-requirements' ╭─[pixi.toml:6:25] 5 │ 6 │ [feature.foobar.run-dependencies] diff --git a/crates/pixi_manifest/src/toml/workspace.rs b/crates/pixi_manifest/src/toml/workspace.rs index 168df61116..c69dc6f3e6 100644 --- a/crates/pixi_manifest/src/toml/workspace.rs +++ b/crates/pixi_manifest/src/toml/workspace.rs @@ -17,7 +17,7 @@ use crate::{ pypi::pypi_options::PypiOptions, toml::{manifest::ExternalWorkspaceProperties, platform::TomlPlatform, preview::TomlPreview}, utils::PixiSpanned, - workspace::{BuildVariantSource, ChannelPriority}, + workspace::{BuildVariantSource, ChannelPriority, SolveStrategy}, }; #[derive(Debug, Clone)] @@ -37,6 +37,7 @@ pub struct TomlWorkspace { pub authors: Option>, pub channels: IndexSet, pub channel_priority: Option, + pub solve_strategy: Option, pub platforms: Spanned>, pub license: Option>, pub license_file: Option>, @@ -130,6 +131,7 @@ impl TomlWorkspace { documentation: self.documentation.or(external.documentation), channels: self.channels, channel_priority: self.channel_priority, + solve_strategy: self.solve_strategy, platforms: self.platforms.value, conda_pypi_map: self.conda_pypi_map, pypi_options: self.pypi_options, @@ -201,6 +203,9 @@ impl<'de> toml_span::Deserialize<'de> for TomlWorkspace { .required::>("channels") .map(TomlIndexSet::into_inner)?; let channel_priority = th.optional("channel-priority"); + let solve_strategy = th + .optional::>>("solve-strategy") + .map(TomlWith::into_inner); let platforms = th .optional::>>>("platforms") .map(TomlWith::into_inner); @@ -252,6 +257,7 @@ impl<'de> toml_span::Deserialize<'de> for TomlWorkspace { authors, channels, channel_priority, + solve_strategy, platforms: platforms.unwrap_or_default(), license, license_file, diff --git a/crates/pixi_manifest/src/workspace.rs b/crates/pixi_manifest/src/workspace.rs index 0fad6fff52..7e1234c898 100644 --- a/crates/pixi_manifest/src/workspace.rs +++ b/crates/pixi_manifest/src/workspace.rs @@ -42,6 +42,9 @@ pub struct Workspace { /// Channel priority for the whole project pub channel_priority: Option, + /// Solve strategy for the whole project. + pub solve_strategy: Option, + /// The platforms this project supports pub platforms: IndexSet, @@ -138,3 +141,50 @@ impl From for ChannelPriority { } } } + +#[derive( + Debug, + Copy, + Clone, + Default, + Eq, + PartialEq, + strum::Display, + strum::VariantNames, + strum::EnumString, + Deserialize, +)] +#[serde(rename_all = "kebab-case")] +#[strum(serialize_all = "kebab-case")] +pub enum SolveStrategy { + #[default] + Highest, + Lowest, + LowestDirect, +} + +impl<'de> toml_span::Deserialize<'de> for SolveStrategy { + fn deserialize(value: &mut Value<'de>) -> Result { + TomlEnum::deserialize(value).map(TomlEnum::into_inner) + } +} + +impl From for rattler_solve::SolveStrategy { + fn from(value: SolveStrategy) -> Self { + match value { + SolveStrategy::Highest => rattler_solve::SolveStrategy::Highest, + SolveStrategy::Lowest => rattler_solve::SolveStrategy::LowestVersion, + SolveStrategy::LowestDirect => rattler_solve::SolveStrategy::LowestVersionDirect, + } + } +} + +impl From for SolveStrategy { + fn from(value: rattler_solve::SolveStrategy) -> Self { + match value { + rattler_solve::SolveStrategy::Highest => Self::Highest, + rattler_solve::SolveStrategy::LowestVersion => Self::Lowest, + rattler_solve::SolveStrategy::LowestVersionDirect => Self::LowestDirect, + } + } +} diff --git a/docs/reference/pixi_manifest.md b/docs/reference/pixi_manifest.md index 08c2674b7b..f3a9fb557b 100644 --- a/docs/reference/pixi_manifest.md +++ b/docs/reference/pixi_manifest.md @@ -212,6 +212,34 @@ channel-priority = "disabled" package = {version = "*", channel = "channel-name"} ``` +### `solve-strategy` (optional) + +This is the setting for the strategy used in the solver step. + +Options: + +- `highest`: **Default**, Solve all packages to the highest compatible version. + +- `lowest`: Solve all packages to the lowest compatible version. + +- `lowest-direct`: Solve only direct dependency packages to the lowest compatible version. Transitive dependencies are still sorted using the `highest` strategy. + +```toml +solve-strategy = "lowest" +``` + +!!! note + When multiple features used in an environment set a specific solve strategy, + the one from the left-most feature declared in the environment is used. + ```toml + [feature.one] + solve-strategy = "lowest" + [feature.two] + solve-strategy = "lowest-direct" + [environments] + combined = ["two", "one"] # <- The solve strategy from feature `two` is used + ``` + ### `requires-pixi` (optional) The required version spec for `pixi` itself to resolve and build the workspace. If unset (**Default**), @@ -874,6 +902,7 @@ The `feature` table allows you to define the following fields per feature. - `platforms`: Same as the [platforms](#platforms). Unless overridden, the `platforms` of the feature will be those defined at workspace level. - `channels`: Same as the [channels](#channels). Unless overridden, the `channels` of the feature will be those defined at workspace level. - `channel-priority`: Same as the [channel-priority](#channel-priority-optional). +- `solve-strategy`: Same as the [solve-strategy](#solve-strategy-optional). - `target`: Same as the [target](#the-target-table). - `tasks`: Same as the [tasks](#the-tasks-table). diff --git a/schema/examples/valid/full.toml b/schema/examples/valid/full.toml index a67bf446e1..b73bbfcd10 100644 --- a/schema/examples/valid/full.toml +++ b/schema/examples/valid/full.toml @@ -15,6 +15,7 @@ platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"] preview = ["pixi-build"] readme = "README.md" repository = "https://github.com/author/project" +solve-strategy = "lowest" version = "0.1.0" [project.pypi-options] @@ -137,10 +138,12 @@ no-build = true [feature.prod] activation = { scripts = ["activate.sh", "deactivate.sh"] } channel-priority = "disabled" +solve-strategy = "lowest-direct" [feature.lint] channel-priority = "strict" dependencies = { flake8 = "3.7.9", black = "19.10b0" } +solve-strategy = "highest" [environments] prod = { features = ["test2"], solve-group = "test" } diff --git a/schema/model.py b/schema/model.py index 56f6d5450b..9edab33d32 100644 --- a/schema/model.py +++ b/schema/model.py @@ -102,6 +102,14 @@ class ChannelPriority(str, Enum): strict = "strict" +class SolveStrategy(str, Enum): + """The strategy used to solve packages.""" + + highest = "highest" + lowest = "lowest" + lowest_direct = "lowest-direct" + + PixiBuildFeature = Annotated[ Literal["pixi-build"], Field(description="Enables building of source records") ] @@ -137,6 +145,14 @@ class Workspace(StrictBaseModel): description="""The type of channel priority that is used in the solve. - 'strict': only take the package from the channel it exist in first. - 'disabled': group all dependencies together as if there is no channel difference.""", + ) + solve_strategy: SolveStrategy | None = Field( + None, + examples=["lowest", "lowest-direct", "highest"], + description="""The strategy that is used in the solve. +- 'highest': solve all packages to the highest compatible version. +- 'lowest': solve all packages to the lowest compatible version. +- 'lowest-direct': solve direct dependencies to the lowest compatible version and transitive ones to the highest compatible version.""", ) exclude_newer: ExcludeNewer | None = Field( None, @@ -527,6 +543,14 @@ class Feature(StrictBaseModel): description="""The type of channel priority that is used in the solve. - 'strict': only take the package from the channel it exist in first. - 'disabled': group all dependencies together as if there is no channel difference.""", + ) + solve_strategy: SolveStrategy | None = Field( + None, + examples=["lowest", "lowest-direct", "highest"], + description="""The strategy that is used in the solve. +- 'highest': solve all packages to the highest compatible version. +- 'lowest': solve all packages to the lowest compatible version. +- 'lowest-direct': solve direct dependencies to the lowest compatible version and transitive ones to the highest compatible version.""", ) platforms: list[Platform] | None = Field( None, diff --git a/schema/schema.json b/schema/schema.json index f8183cda88..403428a0c9 100644 --- a/schema/schema.json +++ b/schema/schema.json @@ -800,6 +800,15 @@ "$ref": "#/$defs/PyPIOptions", "description": "Options related to PyPI indexes for this feature" }, + "solve-strategy": { + "$ref": "#/$defs/SolveStrategy", + "description": "The strategy that is used in the solve.\n- 'highest': solve all packages to the highest compatible version.\n- 'lowest': solve all packages to the lowest compatible version.\n- 'lowest-direct': solve direct dependencies to the lowest compatible version and transitive ones to the highest compatible version.", + "examples": [ + "lowest", + "lowest-direct", + "highest" + ] + }, "system-requirements": { "$ref": "#/$defs/SystemRequirements", "description": "The system requirements of this feature" @@ -1696,6 +1705,16 @@ } } }, + "SolveStrategy": { + "title": "SolveStrategy", + "description": "The strategy used to solve packages.", + "type": "string", + "enum": [ + "highest", + "lowest", + "lowest-direct" + ] + }, "SourceLocation": { "title": "SourceLocation", "description": "The location of a package's source code.", @@ -2320,6 +2339,15 @@ "$ref": "#/$defs/S3Options" } }, + "solve-strategy": { + "$ref": "#/$defs/SolveStrategy", + "description": "The strategy that is used in the solve.\n- 'highest': solve all packages to the highest compatible version.\n- 'lowest': solve all packages to the lowest compatible version.\n- 'lowest-direct': solve direct dependencies to the lowest compatible version and transitive ones to the highest compatible version.", + "examples": [ + "lowest", + "lowest-direct", + "highest" + ] + }, "target": { "title": "Target", "description": "The workspace targets", diff --git a/tests/data/non-satisfiability/mismatch-solve-strategy2/pixi.lock b/tests/data/non-satisfiability/mismatch-solve-strategy2/pixi.lock new file mode 100644 index 0000000000..4fa7223635 --- /dev/null +++ b/tests/data/non-satisfiability/mismatch-solve-strategy2/pixi.lock @@ -0,0 +1,12 @@ +version: 6 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + packages: + win-64: + - conda: https://conda.anaconda.org/conda-forge/win-64/foobar-0.1.0-h2628c8c_0.conda +packages: +- conda: https://conda.anaconda.org/conda-forge/win-64/foobar-0.1.0-h2628c8c_0.conda + sha256: 90553586879bf328f2f9efb8d8faa958ecba822faf379f0a20c3461467b9b955 + md5: defd5d375853a2caff36a19d2d81a28e diff --git a/tests/data/non-satisfiability/mismatch-solve-strategy2/pixi.toml b/tests/data/non-satisfiability/mismatch-solve-strategy2/pixi.toml new file mode 100644 index 0000000000..0e7c73ba67 --- /dev/null +++ b/tests/data/non-satisfiability/mismatch-solve-strategy2/pixi.toml @@ -0,0 +1,9 @@ +[workspace] +solve-strategy = "lowest" +channels = ["conda-forge"] +name = "source-dependency" +platforms = ["win-64"] +version = "0.1.0" + +[dependencies] +foo = { version = "0.1.0" }