Skip to content

Commit

Permalink
feat: update all environments in the lock-file when requesting an env…
Browse files Browse the repository at this point in the history
…ironment (#711)

This is the first implementation of multi-env which will update the
lock-file for all environments.

There are some notable improvements that can be made in terms of
usability from code. I will do that in another PR this PR should
initially be enough to start testing multi-env!

closes #585
  • Loading branch information
baszalmstra authored Jan 29, 2024
1 parent 8dc8dad commit c5f97ae
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 244 deletions.
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions src/cli/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ use crate::{
use clap::Parser;
use itertools::{Either, Itertools};

use indexmap::IndexMap;
use miette::{IntoDiagnostic, WrapErr};
use rattler_conda_types::{
version_spec::{LogicalOperator, RangeOperator},
MatchSpec, NamelessMatchSpec, PackageName, Platform, Version, VersionBumpType, VersionSpec,
Channel, MatchSpec, NamelessMatchSpec, PackageName, Platform, Version, VersionBumpType,
VersionSpec,
};
use rattler_repodata_gateway::sparse::SparseRepoData;
use rattler_solve::{resolvo, SolverImpl};
Expand Down Expand Up @@ -290,7 +292,7 @@ pub async fn add_conda_specs_to_project(
) {
Ok(versions) => versions,
Err(err) => {
return Err(err).wrap_err_with(||miette::miette!(
return Err(err).wrap_err_with(|| miette::miette!(
"could not determine any available versions for {} on {platform}. Either the package could not be found or version constraints on other dependencies result in a conflict.",
new_specs.keys().map(|s| s.as_source()).join(", ")
));
Expand Down Expand Up @@ -353,7 +355,7 @@ pub fn determine_best_version(
project: &Project,
new_specs: &HashMap<PackageName, NamelessMatchSpec>,
new_specs_type: SpecType,
sparse_repo_data: &[SparseRepoData],
sparse_repo_data: &IndexMap<(Channel, Platform), SparseRepoData>,
platform: Platform,
) -> miette::Result<HashMap<PackageName, Version>> {
// Build the combined set of specs while updating the dependencies with the new specs.
Expand All @@ -375,9 +377,12 @@ pub fn determine_best_version(
let package_names = dependencies.names().cloned().collect_vec();

// Get the repodata for the current platform and for NoArch
let platform_sparse_repo_data = sparse_repo_data.iter().filter(|sparse| {
sparse.subdir() == platform.as_str() || sparse.subdir() == Platform::NoArch.as_str()
});
let platform_sparse_repo_data = project
.channels()
.into_iter()
.cloned()
.cartesian_product(vec![platform, Platform::NoArch])
.filter_map(|target| sparse_repo_data.get(&target));

// Load only records we need for this platform
let available_packages = SparseRepoData::load_records_recursive(
Expand Down
13 changes: 7 additions & 6 deletions src/cli/global/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::repodata::friendly_channel_name;
use crate::{config, prefix::Prefix, progress::await_in_progress, repodata::fetch_sparse_repodata};
use clap::Parser;
use dirs::home_dir;
use indexmap::IndexMap;
use itertools::Itertools;
use miette::IntoDiagnostic;
use rattler::install::Transaction;
Expand Down Expand Up @@ -386,25 +387,25 @@ pub async fn execute(args: Args) -> miette::Result<()> {
)
} else {
eprintln!("{whitespace}These apps have been added to {}\n{whitespace} - {script_names}\n\n{} To use them, make sure to add {} to your PATH",
console::style(&bin_dir.display()).bold(),
console::style("!").yellow().bold(),
console::style(&bin_dir.display()).bold()
)
console::style(&bin_dir.display()).bold(),
console::style("!").yellow().bold(),
console::style(&bin_dir.display()).bold()
)
}

Ok(())
}

pub(super) async fn globally_install_package(
package_matchspec: MatchSpec,
platform_sparse_repodata: &[SparseRepoData],
sparse_repodata: &IndexMap<(Channel, Platform), SparseRepoData>,
channel_config: &ChannelConfig,
authenticated_client: ClientWithMiddleware,
) -> miette::Result<(PrefixRecord, Vec<PathBuf>, bool)> {
let package_name = package_name(&package_matchspec)?;

let available_packages = SparseRepoData::load_records_recursive(
platform_sparse_repodata,
sparse_repodata.values(),
vec![package_name.clone()],
None,
)
Expand Down
11 changes: 10 additions & 1 deletion src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::task::{
};
use crate::Project;

use crate::project::manifest::EnvironmentName;
use thiserror::Error;
use tracing::Level;

Expand All @@ -28,13 +29,21 @@ pub struct Args {

#[clap(flatten)]
pub lock_file_usage: super::LockFileUsageArgs,

#[arg(long, short)]
pub environment: Option<String>,
}

/// CLI entry point for `pixi run`
/// When running the sigints are ignored and child can react to them. As it pleases.
pub async fn execute(args: Args) -> miette::Result<()> {
let project = Project::load_or_else_discover(args.manifest_path.as_deref())?;
let environment = project.default_environment();
let environment_name = args
.environment
.map_or_else(|| EnvironmentName::Default, EnvironmentName::Named);
let environment = project
.environment(&environment_name)
.ok_or_else(|| miette::miette!("unknown environment '{environment_name}'"))?;

// Split 'task' into arguments if it's a single string, supporting commands like:
// `"test 1 == 0 || echo failed"` or `"echo foo && echo bar"` or `"echo 'Hello World'"`
Expand Down
37 changes: 21 additions & 16 deletions src/cli/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::Arc;
use std::{cmp::Ordering, path::PathBuf};

use clap::Parser;
use indexmap::IndexMap;
use itertools::Itertools;
use miette::IntoDiagnostic;
use rattler_conda_types::{Channel, ChannelConfig, PackageName, Platform, RepoDataRecord};
Expand Down Expand Up @@ -41,15 +42,15 @@ pub struct Args {
/// fetch packages from `repo_data` based on `filter_func`
fn search_package_by_filter<F>(
package: &PackageName,
repo_data: &[SparseRepoData],
repo_data: Arc<IndexMap<(Channel, Platform), SparseRepoData>>,
filter_func: F,
) -> miette::Result<Vec<RepoDataRecord>>
where
F: Fn(&str, &PackageName) -> bool,
{
let similar_packages = repo_data
.iter()
.flat_map(|repo| {
.flat_map(|(_, repo)| {
repo.package_names()
.filter(|&name| filter_func(name, package))
})
Expand All @@ -59,7 +60,7 @@ where

// search for `similar_packages` in all platform's repodata
// add the latest version of the fetched package to latest_packages vector
for repo in repo_data {
for repo in repo_data.values() {
for package in &similar_packages {
let mut records = repo
.load_records(&PackageName::new_unchecked(*package))
Expand Down Expand Up @@ -104,15 +105,18 @@ pub async fn execute(args: Args) -> miette::Result<()> {
};

let package_name_filter = args.package;

let authenticated_client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
.with_arc(Arc::new(AuthenticationMiddleware::default()))
.build();
let repo_data = fetch_sparse_repodata(
channels.iter().map(AsRef::as_ref),
[Platform::current()],
&authenticated_client,
)
.await?;
let repo_data = Arc::new(
fetch_sparse_repodata(
channels.iter().map(AsRef::as_ref),
[Platform::current()],
&authenticated_client,
)
.await?,
);

// When package name filter contains * (wildcard), it will search and display a list of packages matching this filter
if package_name_filter.contains('*') {
Expand All @@ -135,14 +139,14 @@ pub async fn execute(args: Args) -> miette::Result<()> {

async fn search_exact_package<W: Write>(
package_name: PackageName,
repo_data: Vec<SparseRepoData>,
repo_data: Arc<IndexMap<(Channel, Platform), SparseRepoData>>,
out: W,
) -> miette::Result<()> {
let package_name_search = package_name.clone();
let packages = await_in_progress(
"searching packages",
spawn_blocking(move || {
search_package_by_filter(&package_name_search, &repo_data, |pn, n| {
search_package_by_filter(&package_name_search, repo_data, |pn, n| {
pn == n.as_normalized()
})
}),
Expand Down Expand Up @@ -274,7 +278,7 @@ fn print_package_info<W: Write>(package: &RepoDataRecord, mut out: W) -> io::Res
async fn search_package_by_wildcard<W: Write>(
package_name: PackageName,
package_name_filter: &str,
repo_data: Vec<SparseRepoData>,
repo_data: Arc<IndexMap<(Channel, Platform), SparseRepoData>>,
limit: usize,
out: W,
) -> miette::Result<()> {
Expand All @@ -285,16 +289,17 @@ async fn search_package_by_wildcard<W: Write>(
let mut packages = await_in_progress(
"searching packages",
spawn_blocking(move || {
let packages = search_package_by_filter(&package_name_search, &repo_data, |pn, _| {
wildcard_pattern.is_match(pn)
});
let packages =
search_package_by_filter(&package_name_search, repo_data.clone(), |pn, _| {
wildcard_pattern.is_match(pn)
});
match packages {
Ok(packages) => {
if packages.is_empty() {
let similarity = 0.6;
return search_package_by_filter(
&package_name_search,
&repo_data,
repo_data,
|pn, n| jaro(pn, n.as_normalized()) > similarity,
);
}
Expand Down
11 changes: 10 additions & 1 deletion src/cli/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::path::PathBuf;
use crate::unix::PtySession;

use crate::cli::LockFileUsageArgs;
use crate::project::manifest::EnvironmentName;
#[cfg(target_family = "windows")]
use rattler_shell::shell::CmdExe;

Expand All @@ -25,6 +26,9 @@ pub struct Args {

#[clap(flatten)]
lock_file_usage: LockFileUsageArgs,

#[arg(long, short)]
environment: Option<String>,
}

fn start_powershell(
Expand Down Expand Up @@ -192,7 +196,12 @@ async fn start_nu_shell(

pub async fn execute(args: Args) -> miette::Result<()> {
let project = Project::load_or_else_discover(args.manifest_path.as_deref())?;
let environment = project.default_environment();
let environment_name = args
.environment
.map_or_else(|| EnvironmentName::Default, EnvironmentName::Named);
let environment = project
.environment(&environment_name)
.ok_or_else(|| miette::miette!("unknown environment '{environment_name}'"))?;

// Get the environment variables we need to set activate the project in the shell.
let env = get_activation_env(&environment, args.lock_file_usage.into()).await?;
Expand Down
Loading

0 comments on commit c5f97ae

Please sign in to comment.