Skip to content

Commit

Permalink
fix: search should return latest package across all platforms (#2424)
Browse files Browse the repository at this point in the history
Co-authored-by: Ruben Arts <[email protected]>
  • Loading branch information
nichmor and ruben-arts authored Nov 7, 2024
1 parent 37e29e4 commit acb1ada
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 14 deletions.
30 changes: 19 additions & 11 deletions src/cli/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct Args {
pub package: String,

#[clap(flatten)]
channels: ChannelsConfig,
pub channels: ChannelsConfig,

#[clap(flatten)]
pub project_config: ProjectConfig,
Expand All @@ -45,7 +45,7 @@ pub struct Args {

/// Limit the number of search results
#[clap(short, long)]
limit: Option<usize>,
pub limit: Option<usize>,
}

/// fetch packages from `repo_data` using `repodata_query_func` based on `filter_func`
Expand Down Expand Up @@ -94,10 +94,13 @@ where
latest_packages.extend(records_of_repo.into_values().collect_vec());
}

// sort all versions across all channels and platforms
latest_packages.sort_by(|a, b| a.package_record.version.cmp(&b.package_record.version));

Ok(latest_packages)
}

pub async fn execute(args: Args) -> miette::Result<()> {
pub async fn execute_impl(args: Args) -> miette::Result<Option<Vec<RepoDataRecord>>> {
let stdout = io::stdout();
let project = Project::load_or_else_discover(args.project_config.manifest_path.as_deref()).ok();

Expand Down Expand Up @@ -143,7 +146,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {

// When package name filter contains * (wildcard), it will search and display a
// list of packages matching this filter
if package_name_filter.contains('*') {
let packages = if package_name_filter.contains('*') {
let package_name_without_filter = package_name_filter.replace('*', "");
let package_name = PackageName::try_from(package_name_without_filter).into_diagnostic()?;

Expand All @@ -155,17 +158,22 @@ pub async fn execute(args: Args) -> miette::Result<()> {
args.limit,
stdout,
)
.await?;
.await?
}
// If package name filter doesn't contain * (wildcard), it will search and display specific
// package info (if any package is found)
else {
let package_name = PackageName::try_from(package_name_filter).into_diagnostic()?;

search_exact_package(package_name, all_names, repodata_query_func, stdout).await?;
}
search_exact_package(package_name, all_names, repodata_query_func, stdout).await?
};

Project::warn_on_discovered_from_env(args.project_config.manifest_path.as_deref());
Ok(packages)
}

pub async fn execute(args: Args) -> miette::Result<()> {
execute_impl(args).await?;
Ok(())
}

Expand All @@ -174,7 +182,7 @@ async fn search_exact_package<W: Write, QF, FR>(
all_repodata_names: Vec<PackageName>,
repodata_query_func: QF,
out: W,
) -> miette::Result<()>
) -> miette::Result<Option<Vec<RepoDataRecord>>>
where
QF: Fn(Vec<MatchSpec>) -> FR,
FR: Future<Output = Result<Vec<RepoData>, GatewayError>>,
Expand Down Expand Up @@ -202,7 +210,7 @@ where
}
}

Ok(())
Ok(package.map(|package| vec![package.clone()]))
}

fn print_package_info<W: Write>(package: &RepoDataRecord, mut out: W) -> io::Result<()> {
Expand Down Expand Up @@ -316,7 +324,7 @@ async fn search_package_by_wildcard<W: Write, QF, FR>(
repodata_query_func: QF,
limit: Option<usize>,
out: W,
) -> miette::Result<()>
) -> miette::Result<Option<Vec<RepoDataRecord>>>
where
QF: Fn(Vec<MatchSpec>) -> FR + Clone,
FR: Future<Output = Result<Vec<RepoData>, GatewayError>>,
Expand Down Expand Up @@ -378,7 +386,7 @@ where
}
}

Ok(())
Ok(Some(packages))
}

fn print_matching_packages<W: Write>(
Expand Down
21 changes: 19 additions & 2 deletions tests/integration_rust/common/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ use std::{

use futures::FutureExt;
use pixi::{
cli::{add, cli_config::DependencyConfig, init, install, project, remove, task, update},
cli::{
add, cli_config::DependencyConfig, init, install, project, remove, search, task, update,
},
task::TaskName,
DependencyType,
};
use pixi_manifest::{EnvironmentName, FeatureName, SpecType};
use rattler_conda_types::{NamedChannelOrUrl, Platform};
use rattler_conda_types::{NamedChannelOrUrl, Platform, RepoDataRecord};
use url::Url;

/// Strings from an iterator
Expand Down Expand Up @@ -219,6 +221,21 @@ impl IntoFuture for AddBuilder {
}
}

/// Contains the arguments to pass to [`search::execute()`]. Call `.await` to call
/// the CLI execute method and await the result at the same time.
pub struct SearchBuilder {
pub args: search::Args,
}

impl IntoFuture for SearchBuilder {
type Output = miette::Result<Option<Vec<RepoDataRecord>>>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + 'static>>;

fn into_future(self) -> Self::IntoFuture {
search::execute_impl(self.args).boxed_local()
}
}

/// Contains the arguments to pass to [`remove::execute()`]. Call `.await` to
/// call the CLI execute method and await the result at the same time.
pub struct RemoveBuilder {
Expand Down
22 changes: 21 additions & 1 deletion tests/integration_rust/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use std::{
str::FromStr,
};

use builders::SearchBuilder;
use indicatif::ProgressDrawTarget;
use miette::{Context, Diagnostic, IntoDiagnostic};
use pixi::lock_file::UpdateMode;
use pixi::{
cli::{
add,
Expand All @@ -29,6 +29,10 @@ use pixi::{
},
Project, UpdateLockFileOptions,
};
use pixi::{
cli::{cli_config::ChannelsConfig, search},
lock_file::UpdateMode,
};
use pixi_consts::consts;
use pixi_manifest::{EnvironmentName, FeatureName};
use pixi_progress::global_multi_progress;
Expand Down Expand Up @@ -312,6 +316,22 @@ impl PixiControl {
}
}

/// Search and return latest package. Returns an [`SearchBuilder`].
/// the command and await the result call `.await` on the return value.
pub fn search(&self, name: String) -> SearchBuilder {
SearchBuilder {
args: search::Args {
package: name,
project_config: ProjectConfig {
manifest_path: Some(self.manifest_path()),
},
platform: Platform::current(),
limit: None,
channels: ChannelsConfig::default(),
},
}
}

/// Remove dependencies from the project. Returns a [`RemoveBuilder`].
pub fn remove(&self, spec: &str) -> RemoveBuilder {
RemoveBuilder {
Expand Down
1 change: 1 addition & 0 deletions tests/integration_rust/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod init_tests;
mod install_tests;
mod project_tests;
mod pypi_tests;
mod search_tests;
mod solve_group_tests;
mod task_tests;
mod test_activation;
Expand Down
67 changes: 67 additions & 0 deletions tests/integration_rust/search_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use rattler_conda_types::Platform;
use tempfile::TempDir;
use url::Url;

use crate::common::{
package_database::{Package, PackageDatabase},
PixiControl,
};

#[tokio::test]
async fn search_return_latest_across_everything() {
let mut package_database = PackageDatabase::default();

// Add a package `foo` with 4 different versions, on different platforms
// and different channels
package_database.add_package(Package::build("foo", "1").finish());
package_database.add_package(Package::build("foo", "2").finish());

package_database.add_package(
Package::build("foo", "3")
.with_subdir(Platform::current())
.finish(),
);

let mut latest_package_database = PackageDatabase::default();
latest_package_database.add_package(
Package::build("foo", "4")
.with_subdir(Platform::current())
.finish(),
);

// Write the repodata to disk
let channel_base_dir = TempDir::new().unwrap();
let not_latest_channel_dir = channel_base_dir.path().join("not-latest");
let latest_channel_dir = channel_base_dir.path().join("latest");

package_database
.write_repodata(&not_latest_channel_dir)
.await
.unwrap();

latest_package_database
.write_repodata(&latest_channel_dir)
.await
.unwrap();

let channel_latest = Url::from_file_path(latest_channel_dir).unwrap();
let channel_not_latest = Url::from_file_path(not_latest_channel_dir).unwrap();

let platform = Platform::current();
let pixi = PixiControl::from_manifest(&format!(
r#"
[project]
name = "test-solve-group"
channels = ["{channel_latest}", "{channel_not_latest}"]
platforms = ["{platform}"]
"#
))
.unwrap();

// Search and check that the latest version is returned
let binding = pixi.search("foo".to_string()).await.unwrap().unwrap();
let found_package = binding.last().unwrap();

assert_eq!(found_package.package_record.version.as_str(), "4");
}

0 comments on commit acb1ada

Please sign in to comment.