From 5fbeba5ef3ebccc9577b844672990acbc7f92639 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Wed, 20 Aug 2025 22:28:30 +0200 Subject: [PATCH 01/45] feat: derive default credentials from aws sdk --- Cargo.lock | 2 + crates/rattler_index/Cargo.toml | 2 + crates/rattler_index/src/lib.rs | 106 +++++++++++++++++++++++-------- crates/rattler_index/src/main.rs | 12 +--- 4 files changed, 87 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba9af29361..0b34a44860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4535,6 +4535,8 @@ name = "rattler_index" version = "0.24.7" dependencies = [ "anyhow", + "aws-config", + "aws-sdk-s3", "bytes", "chrono", "clap", diff --git a/crates/rattler_index/Cargo.toml b/crates/rattler_index/Cargo.toml index 3294bf7e05..aa4dfa3d3c 100644 --- a/crates/rattler_index/Cargo.toml +++ b/crates/rattler_index/Cargo.toml @@ -32,6 +32,8 @@ path = "src/main.rs" [dependencies] anyhow = { workspace = true } +aws-config = { workspace = true } +aws-sdk-s3 = { workspace = true } bytes = { workspace = true } chrono = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index af63b68578..ba6780d036 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -3,6 +3,9 @@ #![deny(missing_docs)] use anyhow::{Context, Result}; +use aws_config::meta::region::RegionProviderChain; +use aws_config::BehaviorVersion; +use aws_sdk_s3::config::{Credentials, ProvideCredentials}; use bytes::buf::Buf; use fs_err::{self as fs}; use futures::{stream::FuturesUnordered, StreamExt}; @@ -32,6 +35,7 @@ use std::{ use tokio::sync::Semaphore; use url::Url; +use opendal::services::S3; use opendal::{ layers::RetryLayer, services::{FsConfig, S3Config}, @@ -594,17 +598,23 @@ pub async fn index_fs( pub struct IndexS3Config { /// The channel to index. pub channel: Url, - /// The region of the S3 bucket. - pub region: String, - /// The endpoint URL of the S3 bucket. - pub endpoint_url: Url, + /// The region of the S3 bucket. If not set, the region will be auto detected from the endpoint URL. + pub region: Option, + /// The endpoint URL of the S3 bucket. If not set: + /// - The endpoint will be loaded from `AWS_ENDPOINT_URL` environment variable. + /// - If that is not set, the default endpoint will be used (`https://s3.amazonaws.com`). + pub endpoint_url: Option, /// Whether to force path style for the S3 bucket. - pub force_path_style: bool, + pub force_path_style: Option, /// The access key ID for the S3 bucket. - /// If not set, the authentication storage will be queried. + /// If not set, the access key will be loaded from the environment, if the + /// access key was still not loaded, the authentication storage will be + /// queried. pub access_key_id: Option, /// The secret access key for the S3 bucket. - /// If not set, the authentication storage will be queried. + /// If not set, the access key will be loaded from the environment, if the + /// access key was still not loaded, the authentication storage will be + /// queried. pub secret_access_key: Option, /// The session token for the S3 bucket. /// If not set, the authentication storage will be queried. @@ -644,38 +654,62 @@ pub async fn index_s3( multi_progress, }: IndexS3Config, ) -> anyhow::Result<()> { + // Create the S3 configuration for opendal. let mut s3_config = S3Config::default(); s3_config.root = Some(channel.path().to_string()); s3_config.bucket = channel .host_str() .ok_or(anyhow::anyhow!("No bucket in S3 URL"))? .to_string(); - s3_config.region = Some(region); - s3_config.endpoint = Some(endpoint_url.to_string()); - s3_config.enable_virtual_host_style = !force_path_style; + + // Determine region and endpoint URL. + let endpoint = endpoint_url + .map(|url| url.to_string()) + .or_else(|| std::env::var("AWS_ENDPOINT_URL").ok()) + .unwrap_or_else(|| String::from("https://s3.amazonaws.com")); + + let mut region = region; + if region.is_none() { + // Try to use the AWS SDK to determine the region. + let region_provider = RegionProviderChain::default_provider(); + region = region_provider.region().await.map(|r| r.to_string()); + } + if region.is_none() { + // If no region is provided, we try to detect it from the endpoint URL. + region = S3::detect_region(&endpoint, &s3_config.bucket).await; + } + s3_config.region = region; + s3_config.endpoint = Some(endpoint); + + // How to access the S3 bucket. + s3_config.enable_virtual_host_style = force_path_style.is_none_or(|x| !x); + // Use credentials from the CLI if they are provided. if let (Some(access_key_id), Some(secret_access_key)) = (access_key_id, secret_access_key) { s3_config.secret_access_key = Some(secret_access_key); s3_config.access_key_id = Some(access_key_id); s3_config.session_token = session_token; + } else if let Some((access_key_id, secret_access_key, session_token)) = + load_s3_credentials_from_auth_storage(channel.clone())? + { + // Use the credentials from the authentication storage if they are available. + s3_config.access_key_id = Some(access_key_id); + s3_config.secret_access_key = Some(secret_access_key); + s3_config.session_token = session_token; } else { - // If they're not provided, check rattler authentication storage for credentials. - let auth_storage = AuthenticationStorage::from_env_and_defaults()?; - let auth = auth_storage.get_by_url(channel)?; - if let ( - _, - Some(Authentication::S3Credentials { - access_key_id, - secret_access_key, - session_token, - }), - ) = auth - { - s3_config.access_key_id = Some(access_key_id); - s3_config.secret_access_key = Some(secret_access_key); - s3_config.session_token = session_token; - } + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let Some(credentials_provider) = config.credentials_provider() else { + return Err(anyhow::anyhow!("No AWS credentials provider found",)); + }; + let credentials: Credentials = credentials_provider + .provide_credentials() + .await + .context("failed to determine AWS credentials")?; + s3_config.access_key_id = Some(credentials.access_key_id().to_string()); + s3_config.secret_access_key = Some(credentials.secret_access_key().to_string()); + s3_config.session_token = credentials.session_token().map(ToString::to_string); } + index( target_platform, s3_config, @@ -689,6 +723,26 @@ pub async fn index_s3( .await } +fn load_s3_credentials_from_auth_storage( + channel: Url, +) -> anyhow::Result)>> { + let auth_storage = AuthenticationStorage::from_env_and_defaults()?; + let auth = auth_storage.get_by_url(channel)?; + if let ( + _, + Some(Authentication::S3Credentials { + access_key_id, + secret_access_key, + session_token, + }), + ) = auth + { + Ok(Some((access_key_id, secret_access_key, session_token))) + } else { + Ok(None) + } +} + /// Create a new `repodata.json` for all packages in the given configurator's root. /// If `target_platform` is `Some`, only that specific subdir is indexed. /// Otherwise indexes all subdirs and creates a `repodata.json` for each. diff --git a/crates/rattler_index/src/main.rs b/crates/rattler_index/src/main.rs index bbf0a14dde..c148cc71d2 100644 --- a/crates/rattler_index/src/main.rs +++ b/crates/rattler_index/src/main.rs @@ -159,15 +159,9 @@ async fn main() -> anyhow::Result<()> { let s3_config = config .as_ref() .and_then(|config| config.s3_options.0.get(&bucket)); - let region = region - .or(s3_config.map(|c| c.region.clone())) - .context("S3 region not provided")?; - let endpoint_url = endpoint_url - .or(s3_config.map(|c| c.endpoint_url.clone())) - .context("S3 endpoint url not provided")?; - let force_path_style = force_path_style - .or(s3_config.map(|c| c.force_path_style)) - .context("S3 force-path-style not provided")?; + let region = region.or(s3_config.map(|c| c.region.clone())); + let endpoint_url = endpoint_url.or(s3_config.map(|c| c.endpoint_url.clone())); + let force_path_style = force_path_style.or(s3_config.map(|c| c.force_path_style)); index_s3(IndexS3Config { channel, From 5cc3761000328afc403097fe00435ebff5af93db Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Wed, 20 Aug 2025 22:35:19 +0200 Subject: [PATCH 02/45] fix: cleanup unused features --- Cargo.toml | 13 ++----------- crates/rattler_index/Cargo.toml | 12 ++++++++++-- crates/rattler_networking/Cargo.toml | 13 +++++++++++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 304ef32aa2..f5e29544fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,17 +72,8 @@ generic-array = "0.14.7" getrandom = { version = "0.3.3", default-features = false } glob = "0.3.2" google-cloud-auth = { version = "0.22.0", default-features = false } -aws-config = { version = "=1.5.18", default-features = false, features = [ - "rt-tokio", - "rustls", - "sso", - "credentials-process", -] } -aws-sdk-s3 = { version = "1.85.0", default-features = false, features = [ - "rt-tokio", - "rustls", - "sigv4a", -] } +aws-config = { version = "=1.5.18", default-features = false } +aws-sdk-s3 = { version = "1.85.0", default-features = false} hex = "0.4.3" hex-literal = "1.0.0" http = "1.3" diff --git a/crates/rattler_index/Cargo.toml b/crates/rattler_index/Cargo.toml index aa4dfa3d3c..bc44a13803 100644 --- a/crates/rattler_index/Cargo.toml +++ b/crates/rattler_index/Cargo.toml @@ -32,8 +32,16 @@ path = "src/main.rs" [dependencies] anyhow = { workspace = true } -aws-config = { workspace = true } -aws-sdk-s3 = { workspace = true } +aws-config = { workspace = true, features = [ + "rt-tokio", + "rustls", + "sso", + "credentials-process", +] } +aws-sdk-s3 = { workspace = true, features = [ + "rt-tokio", + "rustls", +] } bytes = { workspace = true } chrono = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } diff --git a/crates/rattler_networking/Cargo.toml b/crates/rattler_networking/Cargo.toml index e073aefa9f..67a8335e8f 100644 --- a/crates/rattler_networking/Cargo.toml +++ b/crates/rattler_networking/Cargo.toml @@ -25,8 +25,17 @@ base64 = { workspace = true } dirs = { workspace = true, optional = true } fs-err = { workspace = true } google-cloud-auth = { workspace = true, optional = true } -aws-config = { workspace = true, optional = true } -aws-sdk-s3 = { workspace = true, optional = true } +aws-config = { workspace = true, optional = true, features = [ + "rt-tokio", + "rustls", + "sso", + "credentials-process", +] } +aws-sdk-s3 = { workspace = true, optional = true, features = [ + "rt-tokio", + "rustls", + "sigv4a", +] } http = { workspace = true } itertools = { workspace = true } keyring = { workspace = true, optional = true, features = [ From ed87abb21122975da659fa425bcdfd56d9e981b2 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:33:08 +0200 Subject: [PATCH 03/45] fix: enable using s3 default credentials with s3middelware --- Cargo.lock | 9 ++ Cargo.toml | 1 + crates/rattler-bin/src/commands/create.rs | 17 +-- crates/rattler_networking/Cargo.toml | 1 + .../rattler_networking/src/s3_middleware.rs | 119 +++++++++++------- .../src/fetch/with_cache.rs | 14 ++- .../src/gateway/sharded_subdir/tokio/index.rs | 11 ++ js-rattler/Cargo.lock | 7 ++ 8 files changed, 121 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dff272e7b6..78545e7f45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.4.0" @@ -4580,6 +4586,8 @@ name = "rattler_index" version = "0.24.10" dependencies = [ "anyhow", + "aws-config", + "aws-sdk-s3", "bytes", "chrono", "clap", @@ -4693,6 +4701,7 @@ name = "rattler_networking" version = "0.25.10" dependencies = [ "anyhow", + "async-once-cell", "async-trait", "aws-config", "aws-sdk-s3", diff --git a/Cargo.toml b/Cargo.toml index a942b57886..7b53ed7d1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ async-compression = { version = "0.4", features = [ ] } async-fd-lock = "0.2.0" fs4 = "0.13.1" +async-once-cell = "0.5.4" async-trait = "0.1.88" axum = { version = "0.8.4", default-features = false, features = [ "tokio", diff --git a/crates/rattler-bin/src/commands/create.rs b/crates/rattler-bin/src/commands/create.rs index 236b1aa6b5..c5c9f3ad42 100644 --- a/crates/rattler-bin/src/commands/create.rs +++ b/crates/rattler-bin/src/commands/create.rs @@ -28,7 +28,7 @@ use rattler_solve::{ libsolv_c::{self}, resolvo, SolverImpl, SolverTask, }; -use reqwest::{Client, Url}; +use reqwest::Client; use crate::global_multi_progress; @@ -173,16 +173,11 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { )) .with_client(download_client.clone()) .with_channel_config(rattler_repodata_gateway::ChannelConfig { - default: SourceConfig::default(), - per_channel: [( - Url::parse("https://prefix.dev")?, - SourceConfig { - sharded_enabled: true, - ..SourceConfig::default() - }, - )] - .into_iter() - .collect(), + default: SourceConfig { + sharded_enabled: true, + ..SourceConfig::default() + }, + per_channel: HashMap::new(), }) .finish(); diff --git a/crates/rattler_networking/Cargo.toml b/crates/rattler_networking/Cargo.toml index 47413d3c84..e24e56a28f 100644 --- a/crates/rattler_networking/Cargo.toml +++ b/crates/rattler_networking/Cargo.toml @@ -20,6 +20,7 @@ system-integration = ["keyring", "netrc-rs", "dirs"] [dependencies] anyhow = { workspace = true } +async-once-cell = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } dirs = { workspace = true, optional = true } diff --git a/crates/rattler_networking/src/s3_middleware.rs b/crates/rattler_networking/src/s3_middleware.rs index d8c16c2ded..57b9e8cb68 100644 --- a/crates/rattler_networking/src/s3_middleware.rs +++ b/crates/rattler_networking/src/s3_middleware.rs @@ -1,10 +1,12 @@ //! Middleware to handle `s3://` URLs to pull artifacts from an S3 bucket -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; -use anyhow::Error; +use anyhow::{Context, Error}; +use async_once_cell::OnceCell; use async_trait::async_trait; -use aws_config::BehaviorVersion; +use aws_config::{meta::region::RegionProviderChain, BehaviorVersion}; use aws_sdk_s3::presigning::PresigningConfig; +use http::Method; use reqwest::{Request, Response}; use reqwest_middleware::{Middleware, Next, Result as MiddlewareResult}; use url::Url; @@ -55,6 +57,7 @@ pub struct S3 { auth_storage: AuthenticationStorage, config: HashMap, expiration: std::time::Duration, + default_client: Arc>, } /// S3 middleware to authenticate requests. @@ -80,15 +83,22 @@ impl S3 { config, auth_storage, expiration: std::time::Duration::from_secs(300), + default_client: Arc::new(OnceCell::new()), } } /// Create an S3 client. /// /// # Arguments /// - /// * `url` - The S3 URL to obtain authentication information from the authentication storage. - /// Only respected for custom (non-AWS-based) configuration. + /// * `url` - The S3 URL to obtain authentication information from the + /// authentication storage. Only respected for custom (non-AWS-based) + /// configuration. pub async fn create_s3_client(&self, url: Url) -> Result { + let sdk_config = self + .default_client + .get_or_init(aws_config::defaults(BehaviorVersion::latest()).load()) + .await; + let bucket_name = url .host_str() .ok_or_else(|| anyhow::anyhow!("host should be present in S3 URL"))?; @@ -111,20 +121,17 @@ impl S3 { secret_access_key, session_token, }), - ) => { - let sdk_config = aws_config::defaults(BehaviorVersion::latest()).load().await; - aws_sdk_s3::config::Builder::from(&sdk_config) - .endpoint_url(endpoint_url) - .region(aws_sdk_s3::config::Region::new(region)) - .force_path_style(force_path_style) - .credentials_provider(aws_sdk_s3::config::Credentials::new( - access_key_id, - secret_access_key, - session_token, - None, - "pixi", - )) - } + ) => aws_sdk_s3::config::Builder::from(sdk_config) + .endpoint_url(endpoint_url) + .region(aws_sdk_s3::config::Region::new(region)) + .force_path_style(force_path_style) + .credentials_provider(aws_sdk_s3::config::Credentials::new( + access_key_id, + secret_access_key, + session_token, + None, + "pixi", + )), (_, Some(_)) => { return Err(anyhow::anyhow!("unsupported authentication method")); } @@ -135,12 +142,15 @@ impl S3 { let s3_config = config_builder.build(); Ok(aws_sdk_s3::Client::from_conf(s3_config)) } else { - let sdk_config = aws_config::defaults(BehaviorVersion::latest()).load().await; - let mut s3_config_builder = aws_sdk_s3::config::Builder::from(&sdk_config); + let mut s3_config_builder = aws_sdk_s3::config::Builder::from(sdk_config); + + // Set the region from the default provider chain. + s3_config_builder.set_region(RegionProviderChain::default_provider().region().await); + // Infer if we expect path-style addressing from the endpoint URL. if let Some(endpoint_url) = sdk_config.endpoint_url() { - // If the endpoint URL is localhost, we probably have to use path-style addressing. - // xref: https://github.com/awslabs/aws-sdk-rust/issues/1230 + // If the endpoint URL is localhost, we probably have to use path-style + // addressing. xref: https://github.com/awslabs/aws-sdk-rust/issues/1230 if endpoint_url.starts_with("http://localhost") { s3_config_builder = s3_config_builder.force_path_style(true); } @@ -149,15 +159,17 @@ impl S3 { s3_config_builder = s3_config_builder.force_path_style(true); } } - let client = aws_sdk_s3::Client::from_conf(s3_config_builder.build()); - Ok(client) + Ok(aws_sdk_s3::Client::from_conf(s3_config_builder.build())) } } /// Generate a presigned S3 `GetObject` request. - async fn generate_presigned_s3_url(&self, url: Url) -> MiddlewareResult { + async fn generate_presigned_s3_url(&self, url: Url, method: &Method) -> MiddlewareResult { let client = self.create_s3_client(url.clone()).await?; + let presign_config = PresigningConfig::expires_in(self.expiration) + .map_err(reqwest_middleware::Error::middleware)?; + let bucket_name = url .host_str() .ok_or_else(|| anyhow::anyhow!("host should be present in S3 URL"))?; @@ -166,19 +178,31 @@ impl S3 { .strip_prefix("/") .ok_or_else(|| anyhow::anyhow!("invalid s3 url: {}", url))?; - let builder = client.get_object().bucket(bucket_name).key(key); - - Url::parse( - builder - .presigned( - PresigningConfig::expires_in(self.expiration) - .map_err(reqwest_middleware::Error::middleware)?, - ) + let presigned_request = match method { + &Method::HEAD => client + .head_object() + .bucket(bucket_name) + .key(key) + .presigned(presign_config) .await - .map_err(reqwest_middleware::Error::middleware)? - .uri(), - ) - .map_err(reqwest_middleware::Error::middleware) + .context("failed to presign S3 HEAD request")?, + &Method::POST => client + .put_object() + .bucket(bucket_name) + .key(key) + .presigned(presign_config) + .await + .context("failed to presign S3 PUT request")?, + _ => client + .get_object() + .bucket(bucket_name) + .key(key) + .presigned(presign_config) + .await + .context("failed to presign S3 GET request")?, + }; + + Ok(Url::parse(presigned_request.uri()).context("failed to parse presigned S3 URL")?) } } @@ -191,12 +215,15 @@ impl Middleware for S3Middleware { extensions: &mut http::Extensions, next: Next<'_>, ) -> MiddlewareResult { - if req.url().scheme() == "s3" { - let url = req.url().clone(); - let presigned_url = self.s3.generate_presigned_s3_url(url).await?; - *req.url_mut() = presigned_url.clone(); + // Only intercept `s3://` requests. + if req.url().scheme() != "s3" { + return next.run(req, extensions).await; } - next.run(req, extensions).await + + let url = req.url().clone(); + let presigned_url = self.s3.generate_presigned_s3_url(url, req.method()).await?; + *req.url_mut() = presigned_url; + next.run(req.try_clone().unwrap(), extensions).await } } @@ -204,13 +231,13 @@ impl Middleware for S3Middleware { mod tests { use std::sync::Arc; - use crate::authentication_storage::backends::file::FileStorage; - - use super::*; use rstest::{fixture, rstest}; use temp_env::async_with_vars; use tempfile::{tempdir, TempDir}; + use super::*; + use crate::authentication_storage::backends::file::FileStorage; + #[tokio::test] async fn test_presigned_s3_request_endpoint_url() { let s3 = S3::new(HashMap::new(), AuthenticationStorage::empty()); diff --git a/crates/rattler_repodata_gateway/src/fetch/with_cache.rs b/crates/rattler_repodata_gateway/src/fetch/with_cache.rs index c4ee2a7a2a..936ffcb09d 100644 --- a/crates/rattler_repodata_gateway/src/fetch/with_cache.rs +++ b/crates/rattler_repodata_gateway/src/fetch/with_cache.rs @@ -396,7 +396,19 @@ pub async fn fetch_repo_data( response.error_for_status().unwrap_err(), ))); } - Ok(response) => response.error_for_status()?, + Ok(response) => { + let response = response.error_for_status()?; + if !response.status().is_success() { + return Err(FetchRepoDataError::HttpError( + reqwest_middleware::Error::Middleware(anyhow::format_err!( + "received unexpected status code ({}) when fetching {}", + response.status(), + repo_data_url.redact(), + )), + )); + } + response + } Err(e) => { return Err(FetchRepoDataError::from(e)); } diff --git a/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs b/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs index f2dfb71455..db8fc65e25 100644 --- a/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs +++ b/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs @@ -6,6 +6,7 @@ use fs_err::tokio as tokio_fs; use futures::{future::OptionFuture, TryFutureExt}; use http::{HeaderMap, Method, Uri}; use http_cache_semantics::{AfterResponse, BeforeRequest, CachePolicy, RequestLike}; +use rattler_redaction::Redact; use reqwest::Response; use reqwest_middleware::ClientWithMiddleware; use serde::{Deserialize, Serialize}; @@ -45,6 +46,16 @@ pub async fn fetch_index( permit: Option>, ) -> Result { let response = response.error_for_status()?; + if !response.status().is_success() { + let mut url = response.url().clone().redact(); + url.set_query(None); + url.set_fragment(None); + return Err(GatewayError::ReqwestMiddlewareError(anyhow::format_err!( + "received unexpected status code ({}) when fetching {}", + response.status(), + url, + ))); + } // Read the bytes of the response let response_url = response.url().clone(); diff --git a/js-rattler/Cargo.lock b/js-rattler/Cargo.lock index fb66b379b1..b6e893db8a 100644 --- a/js-rattler/Cargo.lock +++ b/js-rattler/Cargo.lock @@ -106,6 +106,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-trait" version = "0.1.88" @@ -1897,6 +1903,7 @@ name = "rattler_networking" version = "0.25.10" dependencies = [ "anyhow", + "async-once-cell", "async-trait", "base64", "fs-err", From f12aca4e6891ada6dafde3994d8e6602ea2431a8 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:53:50 +0200 Subject: [PATCH 04/45] upload and fetch too --- Cargo.lock | 4 + Cargo.toml | 1 + crates/rattler-bin/Cargo.toml | 2 + crates/rattler-bin/src/commands/create.rs | 56 +++++--- crates/rattler-bin/src/commands/menu.rs | 30 ++-- .../src/commands/virtual_packages.rs | 6 +- crates/rattler-bin/src/main.rs | 27 ++-- crates/rattler_index/src/lib.rs | 65 +++++---- .../rattler_networking/src/s3_middleware.rs | 6 +- .../src/fetch/with_cache.rs | 25 ++-- crates/rattler_upload/Cargo.toml | 4 +- crates/rattler_upload/src/lib.rs | 3 +- crates/rattler_upload/src/upload/mod.rs | 89 +----------- crates/rattler_upload/src/upload/opt.rs | 50 ++++--- crates/rattler_upload/src/upload/s3.rs | 133 ++++++++++++++++++ 15 files changed, 297 insertions(+), 204 deletions(-) create mode 100644 crates/rattler_upload/src/upload/s3.rs diff --git a/Cargo.lock b/Cargo.lock index 78545e7f45..103e4e6b37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4441,6 +4441,7 @@ dependencies = [ "console 0.16.0", "indicatif", "itertools 0.14.0", + "miette", "once_cell", "rattler", "rattler_cache", @@ -4449,6 +4450,7 @@ dependencies = [ "rattler_networking", "rattler_repodata_gateway", "rattler_solve", + "rattler_upload", "rattler_virtual_packages", "reqwest", "reqwest-middleware", @@ -4916,6 +4918,8 @@ dependencies = [ name = "rattler_upload" version = "0.2.0" dependencies = [ + "aws-config", + "aws-sdk-s3", "base64 0.22.1", "clap", "fs-err", diff --git a/Cargo.toml b/Cargo.toml index 7b53ed7d1f..5e5bd07b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,6 +196,7 @@ rattler_repodata_gateway = { path = "crates/rattler_repodata_gateway", version = rattler_sandbox = { path = "crates/rattler_sandbox", version = "=0.1.10", default-features = false } rattler_shell = { path = "crates/rattler_shell", version = "=0.24.9", default-features = false } rattler_solve = { path = "crates/rattler_solve", version = "=3.0.1", default-features = false } +rattler_upload = { path = "crates/rattler_upload", version = "=0.2.0", default-features = false } rattler_virtual_packages = { path = "crates/rattler_virtual_packages", version = "=2.1.3", default-features = false } # This is also a rattler crate, but we only pin it to minor version diff --git a/crates/rattler-bin/Cargo.toml b/crates/rattler-bin/Cargo.toml index 1a3e9c755f..a769678af4 100644 --- a/crates/rattler-bin/Cargo.toml +++ b/crates/rattler-bin/Cargo.toml @@ -26,6 +26,7 @@ anyhow = { workspace = true } clap = { workspace = true, features = ["derive"] } console = { workspace = true, features = ["windows-console-colors"] } indicatif = { workspace = true } +miette = { workspace = true } once_cell = { workspace = true } rattler = { workspace = true, features = ["indicatif"] } rattler_conda_types = { workspace = true, default-features = false } @@ -35,6 +36,7 @@ rattler_solve = { workspace = true, default-features = false, features = ["resol rattler_virtual_packages = { workspace = true, default-features = false } rattler_cache = { workspace = true, default-features = false } rattler_menuinst = { workspace = true, default-features = false } +rattler_upload = { workspace = true, default-features = false, features = ["s3"]} reqwest = { workspace = true } reqwest-middleware = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/crates/rattler-bin/src/commands/create.rs b/crates/rattler-bin/src/commands/create.rs index c5c9f3ad42..fb297477bb 100644 --- a/crates/rattler-bin/src/commands/create.rs +++ b/crates/rattler-bin/src/commands/create.rs @@ -9,10 +9,10 @@ use std::{ time::{Duration, Instant}, }; -use anyhow::Context; use clap::ValueEnum; use indicatif::{ProgressBar, ProgressStyle}; use itertools::Itertools; +use miette::{Context, IntoDiagnostic}; use rattler::{ default_cache_dir, install::{IndicatifReporter, Installer, Transaction, TransactionOperation}, @@ -98,20 +98,21 @@ impl From for rattler_solve::SolveStrategy { } } -pub async fn create(opt: Opt) -> anyhow::Result<()> { - let channel_config = ChannelConfig::default_with_root_dir(env::current_dir()?); - let current_dir = env::current_dir()?; +pub async fn create(opt: Opt) -> miette::Result<()> { + let channel_config = + ChannelConfig::default_with_root_dir(env::current_dir().into_diagnostic()?); + let current_dir = env::current_dir().into_diagnostic()?; let target_prefix = opt .target_prefix .unwrap_or_else(|| current_dir.join(".prefix")); // Make the target prefix absolute - let target_prefix = std::path::absolute(target_prefix)?; + let target_prefix = std::path::absolute(target_prefix).into_diagnostic()?; println!("Target prefix: {}", target_prefix.display()); // Determine the platform we're going to install for let install_platform = if let Some(platform) = opt.platform { - Platform::from_str(&platform)? + Platform::from_str(&platform).into_diagnostic()? } else { Platform::current() }; @@ -125,12 +126,14 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .specs .iter() .map(|spec| MatchSpec::from_str(spec, ParseStrictness::Strict)) - .collect::, _>>()?; + .collect::, _>>() + .into_diagnostic()?; // Find the default cache directory. Create it if it doesnt exist yet. - let cache_dir = default_cache_dir()?; + let cache_dir = default_cache_dir() + .map_err(|e| miette::miette!("could not determine default cache directory: {}", e))?; std::fs::create_dir_all(&cache_dir) - .map_err(|e| anyhow::anyhow!("could not create cache directory: {}", e))?; + .map_err(|e| miette::miette!("could not create cache directory: {}", e))?; // Determine the channels to use from the command line or select the default. // Like matchspecs this also requires the use of the `channel_config` so we @@ -140,10 +143,12 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .unwrap_or_else(|| vec![String::from("conda-forge")]) .into_iter() .map(|channel_str| Channel::from_str(channel_str, &channel_config)) - .collect::, _>>()?; + .collect::, _>>() + .into_diagnostic()?; // Determine the packages that are currently installed in the environment. - let installed_packages = PrefixRecord::collect_from_prefix::(&target_prefix)?; + let installed_packages = + PrefixRecord::collect_from_prefix::(&target_prefix).into_diagnostic()?; // For each channel/subdirectory combination, download and cache the // `repodata.json` that should be available from the corresponding Url. The @@ -155,11 +160,13 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .expect("failed to create client"); let download_client = reqwest_middleware::ClientBuilder::new(download_client) - .with_arc(Arc::new(AuthenticationMiddleware::from_env_and_defaults()?)) + .with_arc(Arc::new( + AuthenticationMiddleware::from_env_and_defaults().into_diagnostic()?, + )) .with(rattler_networking::OciMiddleware) .with(rattler_networking::S3Middleware::new( HashMap::new(), - AuthenticationStorage::from_env_and_defaults()?, + AuthenticationStorage::from_env_and_defaults().into_diagnostic()?, )) .with(rattler_networking::GCSMiddleware) .build(); @@ -193,6 +200,7 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .recursive(true), ) .await + .into_diagnostic() .context("failed to load repodata")?; // Determine the number of records @@ -213,7 +221,7 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .map(|virt_pkg| { let elems = virt_pkg.split('=').collect::>(); Ok(GenericVirtualPackage { - name: elems[0].try_into()?, + name: elems[0].try_into().into_diagnostic()?, version: elems .get(1) .map_or(Version::from_str("0"), |s| Version::from_str(s)) @@ -221,7 +229,7 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { build_string: (*elems.get(2).unwrap_or(&"")).to_string(), }) }) - .collect::>>()?) + .collect::>>()?) } else { rattler_virtual_packages::VirtualPackage::detect( &rattler_virtual_packages::VirtualPackageOverrides::default(), @@ -232,7 +240,7 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .map(|vpkg| GenericVirtualPackage::from(vpkg.clone())) .collect::>() }) - .map_err(anyhow::Error::from) + .into_diagnostic() } })?; @@ -264,11 +272,11 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { // Next, use a solver to solve this specific problem. This provides us with all // the operations we need to apply to our environment to bring it up to // date. - let solver_result = - wrap_in_progress("solving", move || match opt.solver.unwrap_or_default() { - Solver::Resolvo => resolvo::Solver.solve(solver_task), - Solver::LibSolv => libsolv_c::Solver.solve(solver_task), - })?; + let solver_result = wrap_in_progress("solving", move || match opt.solver.unwrap_or_default() { + Solver::Resolvo => resolvo::Solver.solve(solver_task), + Solver::LibSolv => libsolv_c::Solver.solve(solver_task), + }) + .into_diagnostic()?; let mut required_packages: Vec = solver_result.records; @@ -286,7 +294,8 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { None, None, // ignored packages install_platform, - )?; + ) + .into_diagnostic()?; if transaction.operations.is_empty() { println!("No operations necessary"); @@ -310,7 +319,8 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> { .finish(), ) .install(&target_prefix, required_packages) - .await?; + .await + .into_diagnostic()?; if result.transaction.operations.is_empty() { println!( diff --git a/crates/rattler-bin/src/commands/menu.rs b/crates/rattler-bin/src/commands/menu.rs index cdb2a15858..3a334dcb95 100644 --- a/crates/rattler-bin/src/commands/menu.rs +++ b/crates/rattler-bin/src/commands/menu.rs @@ -1,7 +1,8 @@ -use anyhow::{Context, Result}; +use std::{fs, path::PathBuf}; + use clap::Parser; +use miette::IntoDiagnostic; use rattler_conda_types::{menuinst::MenuMode, PackageName, Platform, PrefixRecord}; -use std::{fs, path::PathBuf}; #[derive(Debug, Parser)] pub struct InstallOpt { @@ -13,47 +14,50 @@ pub struct InstallOpt { package_name: PackageName, } -pub async fn install_menu(opts: InstallOpt) -> Result<()> { +pub async fn install_menu(opts: InstallOpt) -> miette::Result<()> { // Find the prefix record in the target_prefix and call `install_menu` on it - let records: Vec = PrefixRecord::collect_from_prefix(&opts.target_prefix)?; + let records: Vec = + PrefixRecord::collect_from_prefix(&opts.target_prefix).into_diagnostic()?; let record = records .iter() .find(|r| r.repodata_record.package_record.name == opts.package_name) - .with_context(|| { - format!( + .ok_or_else(|| { + miette::miette!( "Package {} not found in prefix {:?}", opts.package_name.as_normalized(), opts.target_prefix ) })?; - let prefix = fs::canonicalize(&opts.target_prefix)?; + let prefix = fs::canonicalize(&opts.target_prefix).into_diagnostic()?; rattler_menuinst::install_menuitems_for_record( &prefix, record, Platform::current(), MenuMode::User, - )?; + ) + .into_diagnostic()?; Ok(()) } -pub async fn remove_menu(opts: InstallOpt) -> Result<()> { +pub async fn remove_menu(opts: InstallOpt) -> miette::Result<()> { // Find the prefix record in the target_prefix and call `remove_menu` on it - let records: Vec = PrefixRecord::collect_from_prefix(&opts.target_prefix)?; + let records: Vec = + PrefixRecord::collect_from_prefix(&opts.target_prefix).into_diagnostic()?; let record = records .iter() .find(|r| r.repodata_record.package_record.name == opts.package_name) - .with_context(|| { - format!( + .ok_or_else(|| { + miette::miette!( "Package {} not found in prefix {:?}", opts.package_name.as_normalized(), opts.target_prefix ) })?; - rattler_menuinst::remove_menu_items(&record.installed_system_menus)?; + rattler_menuinst::remove_menu_items(&record.installed_system_menus).into_diagnostic()?; Ok(()) } diff --git a/crates/rattler-bin/src/commands/virtual_packages.rs b/crates/rattler-bin/src/commands/virtual_packages.rs index 92768b5a7a..8f1f81bceb 100644 --- a/crates/rattler-bin/src/commands/virtual_packages.rs +++ b/crates/rattler-bin/src/commands/virtual_packages.rs @@ -1,12 +1,14 @@ +use miette::IntoDiagnostic; use rattler_conda_types::GenericVirtualPackage; use rattler_virtual_packages::VirtualPackageOverrides; #[derive(Debug, clap::Parser)] pub struct Opt {} -pub fn virtual_packages(_opt: Opt) -> anyhow::Result<()> { +pub fn virtual_packages(_opt: Opt) -> miette::Result<()> { let virtual_packages = - rattler_virtual_packages::VirtualPackage::detect(&VirtualPackageOverrides::default())?; + rattler_virtual_packages::VirtualPackage::detect(&VirtualPackageOverrides::default()) + .into_diagnostic()?; for package in virtual_packages { println!("{}", GenericVirtualPackage::from(package.clone())); } diff --git a/crates/rattler-bin/src/main.rs b/crates/rattler-bin/src/main.rs index ce01cc7407..9e5320e6a4 100644 --- a/crates/rattler-bin/src/main.rs +++ b/crates/rattler-bin/src/main.rs @@ -1,17 +1,20 @@ -use crate::writer::IndicatifWriter; use clap::Parser; use indicatif::{MultiProgress, ProgressDrawTarget}; +use miette::IntoDiagnostic; use once_cell::sync::Lazy; use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter}; +use crate::writer::IndicatifWriter; + mod commands; mod writer; /// Returns a global instance of [`indicatif::MultiProgress`]. /// -/// Although you can always create an instance yourself any logging will interrupt pending -/// progressbars. To fix this issue, logging has been configured in such a way to it will not -/// interfere if you use the [`indicatif::MultiProgress`] returning by this function. +/// Although you can always create an instance yourself any logging will +/// interrupt pending progressbars. To fix this issue, logging has been +/// configured in such a way to it will not interfere if you use the +/// [`indicatif::MultiProgress`] returning by this function. pub fn global_multi_progress() -> MultiProgress { static GLOBAL_MP: Lazy = Lazy::new(|| { let mp = MultiProgress::new(); @@ -41,16 +44,17 @@ enum Command { VirtualPackages(commands::virtual_packages::Opt), InstallMenu(commands::menu::InstallOpt), RemoveMenu(commands::menu::InstallOpt), + Upload(Box), } /// Entry point of the `rattler` cli. #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() -> miette::Result<()> { // Parse the command line arguments let opt = Opt::parse(); - // Determine the logging level based on the the verbose flag and the RUST_LOG environment - // variable. + // Determine the logging level based on the the verbose flag and the RUST_LOG + // environment variable. let default_filter = if opt.verbose { LevelFilter::DEBUG } else { @@ -58,9 +62,10 @@ async fn main() -> anyhow::Result<()> { }; let env_filter = EnvFilter::builder() .with_default_directive(default_filter.into()) - .from_env()? + .from_env() + .into_diagnostic()? // filter logs from apple codesign because they are very noisy - .add_directive("apple_codesign=off".parse()?); + .add_directive("apple_codesign=off".parse().into_diagnostic()?); // Setup the tracing subscriber tracing_subscriber::fmt() @@ -68,7 +73,8 @@ async fn main() -> anyhow::Result<()> { .with_writer(IndicatifWriter::new(global_multi_progress())) .without_time() .finish() - .try_init()?; + .try_init() + .into_diagnostic()?; // Dispatch the selected comment match opt.command { @@ -76,5 +82,6 @@ async fn main() -> anyhow::Result<()> { Command::VirtualPackages(opts) => commands::virtual_packages::virtual_packages(opts), Command::InstallMenu(opts) => commands::menu::install_menu(opts).await, Command::RemoveMenu(opts) => commands::menu::remove_menu(opts).await, + Command::Upload(opts) => rattler_upload::upload_from_args(*opts).await, } } diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index ba6780d036..938d0024af 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -2,15 +2,27 @@ //! files #![deny(missing_docs)] +use std::{ + collections::{HashMap, HashSet}, + io::{BufRead, BufReader, Cursor, Read, Seek}, + path::{Path, PathBuf}, + str::FromStr, + sync::Arc, +}; + use anyhow::{Context, Result}; -use aws_config::meta::region::RegionProviderChain; -use aws_config::BehaviorVersion; +use aws_config::{meta::region::RegionProviderChain, BehaviorVersion}; use aws_sdk_s3::config::{Credentials, ProvideCredentials}; use bytes::buf::Buf; use fs_err::{self as fs}; use futures::{stream::FuturesUnordered, StreamExt}; use fxhash::FxHashMap; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use opendal::{ + layers::RetryLayer, + services::{FsConfig, S3Config, S3}, + Configurator, Operator, +}; use rattler_conda_types::{ package::{ArchiveIdentifier, ArchiveType, IndexJson, PackageFile, RunExportsJson}, ChannelInfo, PackageRecord, PatchInstructions, Platform, RepoData, Shard, ShardedRepodata, @@ -24,24 +36,9 @@ use rattler_package_streaming::{ }; use serde::Serialize; use sha2::{Digest, Sha256}; -use std::io::{BufRead, BufReader}; -use std::{ - collections::{HashMap, HashSet}, - io::{Cursor, Read, Seek}, - path::{Path, PathBuf}, - str::FromStr, - sync::Arc, -}; use tokio::sync::Semaphore; use url::Url; -use opendal::services::S3; -use opendal::{ - layers::RetryLayer, - services::{FsConfig, S3Config}, - Configurator, Operator, -}; - const REPODATA_FROM_PACKAGES: &str = "repodata_from_packages.json"; const REPODATA: &str = "repodata.json"; const REPODATA_SHARDS: &str = "repodata_shards.msgpack.zst"; @@ -439,7 +436,8 @@ pub async fn write_repodata( let unpatched_repodata_path = format!("{subdir}/{REPODATA_FROM_PACKAGES}"); tracing::info!("Writing unpatched repodata to {unpatched_repodata_path}"); let unpatched_repodata_bytes = serde_json::to_vec(&repodata)?; - op.write(&unpatched_repodata_path, unpatched_repodata_bytes) + op.write_with(&unpatched_repodata_path, unpatched_repodata_bytes) + .content_encoding("application/json") .await?; } @@ -464,7 +462,9 @@ pub async fn write_repodata( let repodata_path = format!("{subdir}/{REPODATA}"); tracing::info!("Writing repodata to {repodata_path}"); - op.write(&repodata_path, repodata_bytes).await?; + op.write_with(&repodata_path, repodata_bytes) + .content_encoding("application/json") + .await?; if write_shards { // See CEP 16 @@ -525,7 +525,10 @@ pub async fn write_repodata( let future = async move || { let shard_path = format!("{subdir}/shards/{digest:x}.msgpack.zst"); tracing::trace!("Writing repodata shard to {shard_path}"); - op.write(&shard_path, encoded_shard).await + op.write_with(&shard_path, encoded_shard) + .if_not_exists(true) + .cache_control("public, max-age=31536000, immutable") + .await }; tasks.push(tokio::spawn(future())); } @@ -566,7 +569,8 @@ pub struct IndexFsConfig { pub multi_progress: Option, } -/// Create a new `repodata.json` for all packages in the channel at the given directory. +/// Create a new `repodata.json` for all packages in the channel at the given +/// directory. pub async fn index_fs( IndexFsConfig { channel, @@ -598,10 +602,12 @@ pub async fn index_fs( pub struct IndexS3Config { /// The channel to index. pub channel: Url, - /// The region of the S3 bucket. If not set, the region will be auto detected from the endpoint URL. + /// The region of the S3 bucket. If not set, the region will be auto + /// detected from the endpoint URL. pub region: Option, /// The endpoint URL of the S3 bucket. If not set: - /// - The endpoint will be loaded from `AWS_ENDPOINT_URL` environment variable. + /// - The endpoint will be loaded from `AWS_ENDPOINT_URL` environment + /// variable. /// - If that is not set, the default endpoint will be used (`https://s3.amazonaws.com`). pub endpoint_url: Option, /// Whether to force path style for the S3 bucket. @@ -635,7 +641,8 @@ pub struct IndexS3Config { pub multi_progress: Option, } -/// Create a new `repodata.json` for all packages in the channel at the given S3 URL. +/// Create a new `repodata.json` for all packages in the channel at the given S3 +/// URL. pub async fn index_s3( IndexS3Config { channel, @@ -743,14 +750,14 @@ fn load_s3_credentials_from_auth_storage( } } -/// Create a new `repodata.json` for all packages in the given configurator's root. -/// If `target_platform` is `Some`, only that specific subdir is indexed. +/// Create a new `repodata.json` for all packages in the given configurator's +/// root. If `target_platform` is `Some`, only that specific subdir is indexed. /// Otherwise indexes all subdirs and creates a `repodata.json` for each. /// /// The process is the following: -/// 1. Get all subdirs and create `noarch` and `target_platform` if they do not exist. -/// 2. Iterate subdirs and index each subdir. -/// Therefore, we need to: +/// 1. Get all subdirs and create `noarch` and `target_platform` if they do not +/// exist. +/// 2. Iterate subdirs and index each subdir. Therefore, we need to: /// 1. Collect all uploaded packages in subdir /// 2. Collect all registered packages from `repodata.json` (if exists) /// 3. Determine which packages to add to and to delete from `repodata.json` diff --git a/crates/rattler_networking/src/s3_middleware.rs b/crates/rattler_networking/src/s3_middleware.rs index 57b9e8cb68..ab07b7d62d 100644 --- a/crates/rattler_networking/src/s3_middleware.rs +++ b/crates/rattler_networking/src/s3_middleware.rs @@ -178,15 +178,15 @@ impl S3 { .strip_prefix("/") .ok_or_else(|| anyhow::anyhow!("invalid s3 url: {}", url))?; - let presigned_request = match method { - &Method::HEAD => client + let presigned_request = match *method { + Method::HEAD => client .head_object() .bucket(bucket_name) .key(key) .presigned(presign_config) .await .context("failed to presign S3 HEAD request")?, - &Method::POST => client + Method::POST => client .put_object() .bucket(bucket_name) .key(key) diff --git a/crates/rattler_repodata_gateway/src/fetch/with_cache.rs b/crates/rattler_repodata_gateway/src/fetch/with_cache.rs index 936ffcb09d..275f228cf5 100644 --- a/crates/rattler_repodata_gateway/src/fetch/with_cache.rs +++ b/crates/rattler_repodata_gateway/src/fetch/with_cache.rs @@ -396,19 +396,7 @@ pub async fn fetch_repo_data( response.error_for_status().unwrap_err(), ))); } - Ok(response) => { - let response = response.error_for_status()?; - if !response.status().is_success() { - return Err(FetchRepoDataError::HttpError( - reqwest_middleware::Error::Middleware(anyhow::format_err!( - "received unexpected status code ({}) when fetching {}", - response.status(), - repo_data_url.redact(), - )), - )); - } - response - } + Ok(response) => response.error_for_status()?, Err(e) => { return Err(FetchRepoDataError::from(e)); } @@ -444,6 +432,17 @@ pub async fn fetch_repo_data( }); } + // Fail if the status code is not a success + if !response.status().is_success() { + return Err(FetchRepoDataError::HttpError( + reqwest_middleware::Error::Middleware(anyhow::format_err!( + "received unexpected status code ({}) when fetching {}", + response.status(), + repo_data_url.redact(), + )), + )); + } + // Get cache headers from the response let cache_headers = CacheHeaders::from(&response); diff --git a/crates/rattler_upload/Cargo.toml b/crates/rattler_upload/Cargo.toml index 58d5e398cb..e4a484e488 100644 --- a/crates/rattler_upload/Cargo.toml +++ b/crates/rattler_upload/Cargo.toml @@ -11,7 +11,7 @@ license.workspace = true readme.workspace = true [features] -s3 = ["rattler_networking/s3"] +s3 = ["rattler_networking/s3", "aws-config", "aws-sdk-s3"] [dependencies] rattler_conda_types = { workspace = true, default-features = false } @@ -49,6 +49,8 @@ tokio = { version = "1.44.2", features = [ "rt-multi-thread", "process", ] } +aws-config = { workspace = true, optional = true } +aws-sdk-s3 = { workspace = true, optional = true } [target.'cfg(not(target_os = "windows"))'.dependencies] sha2 = { version = "0.10.8", features = ["asm"] } diff --git a/crates/rattler_upload/src/lib.rs b/crates/rattler_upload/src/lib.rs index 84e4f19f70..23a088cd4a 100644 --- a/crates/rattler_upload/src/lib.rs +++ b/crates/rattler_upload/src/lib.rs @@ -1,13 +1,13 @@ pub mod upload; pub(crate) mod utils; -use crate::utils::tool_configuration; use miette::IntoDiagnostic; use rattler_conda_types::package::ArchiveType; use upload::opt::{ AnacondaData, ArtifactoryData, CondaForgeData, PrefixData, QuetzData, ServerType, UploadOpts, }; +use crate::utils::tool_configuration; /// Upload package to different channels pub async fn upload_from_args(args: UploadOpts) -> miette::Result<()> { // Validate package files are provided @@ -61,6 +61,7 @@ pub async fn upload_from_args(args: UploadOpts) -> miette::Result<()> { s3_opts.secret_access_key, s3_opts.session_token, &args.package_files, + s3_opts.force, ) .await } diff --git a/crates/rattler_upload/src/upload/mod.rs b/crates/rattler_upload/src/upload/mod.rs index 0dc51054f4..965bb13e80 100644 --- a/crates/rattler_upload/src/upload/mod.rs +++ b/crates/rattler_upload/src/upload/mod.rs @@ -26,12 +26,11 @@ pub mod conda_forge; pub mod opt; mod package; mod prefix; -mod trusted_publishing; - #[cfg(feature = "s3")] -use opendal::{services::S3Config, Configurator, Operator}; +mod s3; +mod trusted_publishing; #[cfg(feature = "s3")] -use std::net::Ipv4Addr; +pub use s3::upload_package_to_s3; pub use prefix::upload_package_to_prefix; @@ -270,88 +269,6 @@ pub async fn upload_package_to_anaconda( } Ok(()) } -#[cfg(feature = "s3")] -/// Uploads a package to a channel in an S3 bucket. -#[allow(clippy::too_many_arguments)] -pub async fn upload_package_to_s3( - storage: &AuthenticationStorage, - channel: Url, - endpoint_url: Url, - region: String, - force_path_style: bool, - access_key_id: Option, - secret_access_key: Option, - session_token: Option, - package_files: &Vec, -) -> miette::Result<()> { - let bucket = channel - .host_str() - .ok_or_else(|| miette::miette!("Failed to get host from channel URL"))?; - - if let Some(host_endpoint) = endpoint_url.host_str() { - if host_endpoint.parse::().is_ok() && !force_path_style { - return Err(miette::miette!( - "Endpoint URL {} (IPv4 address) cannot be used without path style, please use --force-path-style", - endpoint_url - )); - } - } - - let mut s3_config = S3Config::default(); - s3_config.root = Some(channel.path().to_string()); - s3_config.bucket = bucket.to_string(); - s3_config.region = Some(region); - s3_config.endpoint = Some(endpoint_url.to_string()); - s3_config.enable_virtual_host_style = !force_path_style; - // Use credentials from the CLI if they are provided. - if let (Some(access_key_id), Some(secret_access_key)) = (access_key_id, secret_access_key) { - s3_config.secret_access_key = Some(secret_access_key); - s3_config.access_key_id = Some(access_key_id); - s3_config.session_token = session_token; - } else { - // If they're not provided, check rattler authentication storage for credentials. - let auth = storage.get_by_url(channel.clone()).into_diagnostic()?; - if let ( - _, - Some(Authentication::S3Credentials { - access_key_id, - secret_access_key, - session_token, - }), - ) = auth - { - s3_config.access_key_id = Some(access_key_id); - s3_config.secret_access_key = Some(secret_access_key); - s3_config.session_token = session_token; - } - } - - let builder = s3_config.into_builder(); - let op = Operator::new(builder).into_diagnostic()?.finish(); - - for package_file in package_files { - let package = ExtractedPackage::from_package_file(package_file)?; - let subdir = package - .subdir() - .ok_or_else(|| miette::miette!("Failed to get subdir"))?; - let filename = package - .filename() - .ok_or_else(|| miette::miette!("Failed to get filename"))?; - let key = format!("{subdir}/{filename}"); - let body = fs::read(package_file).await.into_diagnostic()?; - op.write_with(&key, body) - .if_not_exists(true) - .await - .into_diagnostic()?; - - tracing::info!( - "Uploaded package to s3://{bucket}{}/{key}", - channel.path().to_string() - ); - } - - Ok(()) -} async fn send_request_with_retry( prepared_request: reqwest::RequestBuilder, diff --git a/crates/rattler_upload/src/upload/opt.rs b/crates/rattler_upload/src/upload/opt.rs index 2d849d37b9..213db94a1f 100644 --- a/crates/rattler_upload/src/upload/opt.rs +++ b/crates/rattler_upload/src/upload/opt.rs @@ -1,18 +1,19 @@ //! Command-line options. -use clap::{arg, Parser}; -use rattler_conda_types::utils::url_with_trailing_slash::UrlWithTrailingSlash; -use rattler_conda_types::{NamedChannelOrUrl, Platform}; -use rattler_networking::mirror_middleware; -use rattler_networking::AuthenticationStorage; -use rattler_solve::ChannelPriority; use std::{collections::HashMap, path::PathBuf, str::FromStr}; -use tracing::warn; -use url::Url; +use clap::{arg, Parser}; +use rattler_conda_types::{ + utils::url_with_trailing_slash::UrlWithTrailingSlash, NamedChannelOrUrl, Platform, +}; #[cfg(feature = "s3")] use rattler_networking::s3_middleware; +use rattler_networking::{mirror_middleware, AuthenticationStorage}; +use rattler_solve::ChannelPriority; +use tracing::warn; +use url::Url; -/// The configuration type for rattler-build - just extends rattler / pixi config and can load the same TOML files. +/// The configuration type for rattler-build - just extends rattler / pixi +/// config and can load the same TOML files. pub type Config = rattler_config::config::ConfigBase<()>; /// Container for `rattler_solver::ChannelPriority` so that it can be parsed @@ -98,7 +99,8 @@ impl CommonData { allow_insecure_host: Option>, ) -> Self { // mirror config - // todo: this is a duplicate in pixi and pixi-pack: do it like in `compute_s3_config` + // todo: this is a duplicate in pixi and pixi-pack: do it like in + // `compute_s3_config` let mut mirror_config = HashMap::new(); tracing::debug!("Using mirrors: {:?}", config.mirrors); @@ -422,25 +424,22 @@ fn parse_s3_url(value: &str) -> Result { /// Options for uploading to S3 #[derive(Clone, Debug, PartialEq, Parser)] pub struct S3Opts { - /// The channel URL in the S3 bucket to upload the package to, e.g., `s3://my-bucket/my-channel` + /// The channel URL in the S3 bucket to upload the package to, e.g., + /// `s3://my-bucket/my-channel` #[arg(short, long, env = "S3_CHANNEL", value_parser = parse_s3_url)] pub channel: Url, /// The endpoint URL of the S3 backend - #[arg( - long, - env = "S3_ENDPOINT_URL", - default_value = "https://s3.amazonaws.com" - )] - pub endpoint_url: Url, + #[arg(long, env = "S3_ENDPOINT_URL")] + pub endpoint_url: Option, /// The region of the S3 backend - #[arg(long, env = "S3_REGION", default_value = "eu-central-1")] - pub region: String, + #[arg(long, env = "S3_REGION")] + pub region: Option, /// Whether to use path-style S3 URLs #[arg(long, env = "S3_FORCE_PATH_STYLE", default_value = "false")] - pub force_path_style: bool, + pub force_path_style: Option, /// The access key ID for the S3 bucket. #[arg(long, env = "S3_ACCESS_KEY_ID", requires_all = ["secret_access_key"])] @@ -453,6 +452,10 @@ pub struct S3Opts { /// The session token for the S3 bucket. #[arg(long, env = "S3_SESSION_TOKEN", requires_all = ["access_key_id", "secret_access_key"])] pub session_token: Option, + + /// Replace files if it already exists. + #[arg(long)] + pub force: bool, } #[derive(Debug)] @@ -623,7 +626,8 @@ pub struct DebugOpts { #[clap(flatten)] pub common: CommonOpts, - /// Name of the specific output to debug (only required when a recipe has multiple outputs) + /// Name of the specific output to debug (only required when a recipe has + /// multiple outputs) #[arg(long, help = "Name of the specific output to debug")] pub output_name: Option, } @@ -650,8 +654,8 @@ pub struct DebugData { } impl DebugData { - /// Generate a new `TestData` struct from `TestOpts` and an optional pixi config. - /// `TestOpts` have higher priority than the pixi config. + /// Generate a new `TestData` struct from `TestOpts` and an optional pixi + /// config. `TestOpts` have higher priority than the pixi config. pub fn from_opts_and_config(opts: DebugOpts, config: Option) -> Self { Self { recipe_path: opts.recipe, diff --git a/crates/rattler_upload/src/upload/s3.rs b/crates/rattler_upload/src/upload/s3.rs new file mode 100644 index 0000000000..6ab4a75470 --- /dev/null +++ b/crates/rattler_upload/src/upload/s3.rs @@ -0,0 +1,133 @@ +use std::path::PathBuf; + +use aws_config::{meta::region::RegionProviderChain, BehaviorVersion}; +use aws_sdk_s3::config::{Credentials, ProvideCredentials}; +use miette::{Context, IntoDiagnostic}; +use opendal::{ + services::{S3Config, S3}, + Configurator, Operator, +}; +use rattler_networking::{Authentication, AuthenticationStorage}; +use url::Url; + +use crate::upload::package::ExtractedPackage; + +/// Uploads a package to a channel in an S3 bucket. +#[allow(clippy::too_many_arguments)] +pub async fn upload_package_to_s3( + auth_storage: &AuthenticationStorage, + channel: Url, + endpoint_url: Option, + region: Option, + force_path_style: Option, + access_key_id: Option, + secret_access_key: Option, + session_token: Option, + package_files: &Vec, + force: bool, +) -> miette::Result<()> { + let bucket = channel + .host_str() + .ok_or(miette::miette!("No bucket in S3 URL"))?; + + // Create the S3 configuration for opendal. + let mut s3_config = S3Config::default(); + s3_config.root = Some(channel.path().to_string()); + s3_config.bucket = bucket.to_string(); + + // Determine region and endpoint URL. + let endpoint = endpoint_url + .map(|url| url.to_string()) + .or_else(|| std::env::var("AWS_ENDPOINT_URL").ok()) + .unwrap_or_else(|| String::from("https://s3.amazonaws.com")); + + let mut region = region; + if region.is_none() { + // Try to use the AWS SDK to determine the region. + let region_provider = RegionProviderChain::default_provider(); + region = region_provider.region().await.map(|r| r.to_string()); + } + if region.is_none() { + // If no region is provided, we try to detect it from the endpoint URL. + region = S3::detect_region(&endpoint, &s3_config.bucket).await; + } + s3_config.region = region; + s3_config.endpoint = Some(endpoint); + + // How to access the S3 bucket. + s3_config.enable_virtual_host_style = force_path_style.is_none_or(|x| !x); + + // Use credentials from the CLI if they are provided. + if let (Some(access_key_id), Some(secret_access_key)) = (access_key_id, secret_access_key) { + s3_config.secret_access_key = Some(secret_access_key); + s3_config.access_key_id = Some(access_key_id); + s3_config.session_token = session_token; + } else if let Some((access_key_id, secret_access_key, session_token)) = + load_s3_credentials_from_auth_storage(auth_storage, channel.clone())? + { + // Use the credentials from the authentication storage if they are available. + s3_config.access_key_id = Some(access_key_id); + s3_config.secret_access_key = Some(secret_access_key); + s3_config.session_token = session_token; + } else { + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let Some(credentials_provider) = config.credentials_provider() else { + return Err(miette::miette!("No AWS credentials provider found",)); + }; + let credentials: Credentials = credentials_provider + .provide_credentials() + .await + .into_diagnostic() + .context("failed to determine AWS credentials")?; + s3_config.access_key_id = Some(credentials.access_key_id().to_string()); + s3_config.secret_access_key = Some(credentials.secret_access_key().to_string()); + s3_config.session_token = credentials.session_token().map(ToString::to_string); + } + + let builder = s3_config.into_builder(); + let op = Operator::new(builder).into_diagnostic()?.finish(); + + for package_file in package_files { + let package = ExtractedPackage::from_package_file(package_file)?; + let subdir = package + .subdir() + .ok_or_else(|| miette::miette!("Failed to get subdir"))?; + let filename = package + .filename() + .ok_or_else(|| miette::miette!("Failed to get filename"))?; + let key = format!("{subdir}/{filename}"); + let body = fs_err::tokio::read(package_file).await.into_diagnostic()?; + op.write_with(&key, body) + .content_disposition(&format!("attachment; filename={filename}")) + .if_not_exists(!force) + .await + .into_diagnostic()?; + + tracing::info!( + "Uploaded package to s3://{bucket}{}/{key}", + channel.path().to_string() + ); + } + + Ok(()) +} + +fn load_s3_credentials_from_auth_storage( + auth_storage: &AuthenticationStorage, + channel: Url, +) -> miette::Result)>> { + let auth = auth_storage.get_by_url(channel).into_diagnostic()?; + if let ( + _, + Some(Authentication::S3Credentials { + access_key_id, + secret_access_key, + session_token, + }), + ) = auth + { + Ok(Some((access_key_id, secret_access_key, session_token))) + } else { + Ok(None) + } +} From 487a1090a60026df1a10fd160d18413ad56df437 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:11:47 +0200 Subject: [PATCH 05/45] fix: dont error on precondition failure --- crates/rattler_index/src/lib.rs | 13 +++++++++++-- crates/rattler_upload/src/upload/s3.rs | 26 ++++++++++++++++++-------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index 938d0024af..05cbcd0758 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -21,7 +21,7 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use opendal::{ layers::RetryLayer, services::{FsConfig, S3Config, S3}, - Configurator, Operator, + Configurator, ErrorKind, Operator, }; use rattler_conda_types::{ package::{ArchiveIdentifier, ArchiveType, IndexJson, PackageFile, RunExportsJson}, @@ -525,10 +525,19 @@ pub async fn write_repodata( let future = async move || { let shard_path = format!("{subdir}/shards/{digest:x}.msgpack.zst"); tracing::trace!("Writing repodata shard to {shard_path}"); - op.write_with(&shard_path, encoded_shard) + match op + .write_with(&shard_path, encoded_shard) .if_not_exists(true) .cache_control("public, max-age=31536000, immutable") .await + { + Err(e) if e.kind() == ErrorKind::ConditionNotMatch => { + tracing::trace!("{shard_path} already exists"); + Ok(()) + } + Ok(_metadata) => Ok(()), + Err(e) => Err(e), + } }; tasks.push(tokio::spawn(future())); } diff --git a/crates/rattler_upload/src/upload/s3.rs b/crates/rattler_upload/src/upload/s3.rs index 6ab4a75470..8e2b6976d5 100644 --- a/crates/rattler_upload/src/upload/s3.rs +++ b/crates/rattler_upload/src/upload/s3.rs @@ -5,7 +5,7 @@ use aws_sdk_s3::config::{Credentials, ProvideCredentials}; use miette::{Context, IntoDiagnostic}; use opendal::{ services::{S3Config, S3}, - Configurator, Operator, + Configurator, ErrorKind, Operator, }; use rattler_networking::{Authentication, AuthenticationStorage}; use url::Url; @@ -97,16 +97,26 @@ pub async fn upload_package_to_s3( .ok_or_else(|| miette::miette!("Failed to get filename"))?; let key = format!("{subdir}/{filename}"); let body = fs_err::tokio::read(package_file).await.into_diagnostic()?; - op.write_with(&key, body) + match op + .write_with(&key, body) .content_disposition(&format!("attachment; filename={filename}")) .if_not_exists(!force) .await - .into_diagnostic()?; - - tracing::info!( - "Uploaded package to s3://{bucket}{}/{key}", - channel.path().to_string() - ); + { + Err(e) if e.kind() == ErrorKind::ConditionNotMatch => { + tracing::info!( + "Skipped package s3://{bucket}{}/{key}, the package already exists. Use --force to overwrite.", + channel.path().to_string() + ); + } + Ok(_metadata) => { + tracing::info!( + "Uploaded package to s3://{bucket}{}/{key}", + channel.path().to_string() + ); + } + Err(e) => return Err(e).into_diagnostic(), + } } Ok(()) From a3067f8ec3b25bab16eb9b49e775dec46cdbb6fb Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:27:18 +0200 Subject: [PATCH 06/45] fix: test compilation and pre-commit --- Cargo.toml | 4 +- .../rattler_networking/src/s3_middleware.rs | 7 ++++ crates/rattler_upload/Cargo.toml | 40 +++++++++---------- py-rattler/Cargo.lock | 16 ++++++-- py-rattler/README.md | 2 +- 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e5bd07b7e..242ea3aa83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ chrono = { version = "0.4.41", default-features = false, features = [ "serde", "alloc", ] } -clap = { version = "4.5.38", features = ["derive"] } +clap = { version = "4.5.38", features = ["derive", "color", "suggestions", "error-context"] } clap-verbosity-flag = "3.0.3" cmake = "0.1.54" console = { version = "0.16.0", features = ["windows-console-colors"] } @@ -74,7 +74,7 @@ getrandom = { version = "0.3.3", default-features = false } glob = "0.3.2" google-cloud-auth = { version = "0.22.0", default-features = false } aws-config = { version = "=1.5.18", default-features = false } -aws-sdk-s3 = { version = "1.85.0", default-features = false} +aws-sdk-s3 = { version = "1.85.0", default-features = false } hex = "0.4.3" hex-literal = "1.0.0" http = "1.3" diff --git a/crates/rattler_networking/src/s3_middleware.rs b/crates/rattler_networking/src/s3_middleware.rs index ab07b7d62d..e313368746 100644 --- a/crates/rattler_networking/src/s3_middleware.rs +++ b/crates/rattler_networking/src/s3_middleware.rs @@ -251,6 +251,7 @@ mod tests { async { s3.generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await .unwrap() @@ -277,6 +278,7 @@ mod tests { async { s3.generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await .unwrap() @@ -325,6 +327,7 @@ region = eu-central-1 async { s3.generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await .unwrap() @@ -349,6 +352,7 @@ region = eu-central-1 async { s3.generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await .unwrap() @@ -393,6 +397,7 @@ region = eu-central-1 let presigned = s3 .generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await .unwrap(); @@ -422,6 +427,7 @@ region = eu-central-1 let result = s3 .generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await; assert!(result.is_err()); @@ -448,6 +454,7 @@ region = eu-central-1 let result = s3 .generate_presigned_s3_url( Url::parse("s3://rattler-s3-testing/channel/noarch/repodata.json").unwrap(), + &Method::GET, ) .await; assert!(result.is_err()); diff --git a/crates/rattler_upload/Cargo.toml b/crates/rattler_upload/Cargo.toml index e4a484e488..f36c8bd403 100644 --- a/crates/rattler_upload/Cargo.toml +++ b/crates/rattler_upload/Cargo.toml @@ -21,29 +21,29 @@ rattler_redaction = { workspace = true, default-features = false } rattler_package_streaming = { workspace = true, default-features = false } rattler_config = { workspace = true, default-features = false } rattler_solve = { workspace = true } -miette = { version = "7.6.0", features = ["fancy"] } -clap = { version = "4.5.37", features = ["derive", "env", "cargo"] } +miette = { workspace = true, features = ["fancy"] } +clap = { workspace = true } fs-err = { workspace = true, features = ["tokio"] } -futures = "0.3.31" -indicatif = "0.18.0" -opendal = { version = "0.54.0", default-features = false, features = [ +futures = { workspace = true } +indicatif = { workspace = true } +opendal = { workspace = true , default-features = false, features = [ "services-s3", ]} -reqwest-retry = "0.7.0" -tokio-util = { version = "0.7.15", features = ["codec", "compat"] } -reqwest = { version = "0.12.15", default-features = false, features = [ +reqwest-retry = { workspace = true } +tokio-util = { workspace = true, features = ["codec", "compat"] } +reqwest = { workspace = true, default-features = false, features = [ "multipart", ]} -url = "2.5.4" -tracing = "0.1.41" -reqwest-middleware = { version = "0.4.2", features = ["json"] } -serde_yaml = "0.9.34" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" -base64 = "0.22.1" -tempfile = "3.19.1" -thiserror = "2.0.12" -tokio = { version = "1.44.2", features = [ +url = { workspace = true } +tracing = { workspace = true } +reqwest-middleware = { workspace = true, features = ["json"] } +serde_yaml = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +base64 = { workspace = true } +tempfile = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true, features = [ "rt", "macros", "rt-multi-thread", @@ -53,7 +53,7 @@ aws-config = { workspace = true, optional = true } aws-sdk-s3 = { workspace = true, optional = true } [target.'cfg(not(target_os = "windows"))'.dependencies] -sha2 = { version = "0.10.8", features = ["asm"] } +sha2 = { workspace = true, features = ["asm"] } [target.'cfg(target_os = "windows")'.dependencies] -sha2 = { version = "0.10.8" } +sha2 = { workspace = true } diff --git a/py-rattler/Cargo.lock b/py-rattler/Cargo.lock index 18b704fecc..63713c366b 100644 --- a/py-rattler/Cargo.lock +++ b/py-rattler/Cargo.lock @@ -259,6 +259,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.4.0" @@ -3308,9 +3314,10 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path_resolver" -version = "0.1.2" +version = "0.2.0" dependencies = [ "fs-err", + "fxhash", "indexmap 2.10.0", "itertools 0.14.0", "proptest", @@ -3897,7 +3904,7 @@ dependencies = [ [[package]] name = "rattler" -version = "0.34.13" +version = "0.35.0" dependencies = [ "anyhow", "console", @@ -4040,9 +4047,11 @@ dependencies = [ [[package]] name = "rattler_index" -version = "0.24.9" +version = "0.24.10" dependencies = [ "anyhow", + "aws-config", + "aws-sdk-s3", "bytes", "chrono", "clap", @@ -4136,6 +4145,7 @@ name = "rattler_networking" version = "0.25.10" dependencies = [ "anyhow", + "async-once-cell", "async-trait", "aws-config", "aws-sdk-s3", diff --git a/py-rattler/README.md b/py-rattler/README.md index 32d46ee883..94389aee61 120000 --- a/py-rattler/README.md +++ b/py-rattler/README.md @@ -1 +1 @@ -../README.md \ No newline at end of file +../README.md From d92dcaaa62925b1bd2ad43e6057d333b1a10f3d3 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:29:41 +0200 Subject: [PATCH 07/45] fix: README --- py-rattler/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py-rattler/README.md b/py-rattler/README.md index 94389aee61..32d46ee883 120000 --- a/py-rattler/README.md +++ b/py-rattler/README.md @@ -1 +1 @@ -../README.md +../README.md \ No newline at end of file From d3357cbe22a74b05c5e49353c6fe21b122777a3c Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:38:25 +0200 Subject: [PATCH 08/45] fix: update py-rattler --- py-rattler/rattler/index/index.py | 4 ++-- py-rattler/src/index.rs | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/py-rattler/rattler/index/index.py b/py-rattler/rattler/index/index.py index 997181b4ed..d4b70155ed 100644 --- a/py-rattler/rattler/index/index.py +++ b/py-rattler/rattler/index/index.py @@ -46,8 +46,8 @@ async def index_fs( async def index_s3( channel_url: str, - region: str, - endpoint_url: str, + region: Optional[str] = None, + endpoint_url: Optional[str] = None, force_path_style: bool = False, access_key_id: Optional[str] = None, secret_access_key: Optional[str] = None, diff --git a/py-rattler/src/index.rs b/py-rattler/src/index.rs index 3bc0f703ee..4eadd1d7e2 100644 --- a/py-rattler/src/index.rs +++ b/py-rattler/src/index.rs @@ -41,13 +41,13 @@ pub fn py_index_fs( #[pyfunction] #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] -#[pyo3(signature = (channel_url, region, endpoint_url, force_path_style, access_key_id=None,secret_access_key=None, session_token=None, target_platform=None, repodata_patch=None, write_zst=true, write_shards=true, force=false, max_parallel=None))] +#[pyo3(signature = (channel_url, region=None, endpoint_url=None, force_path_style=None, access_key_id=None,secret_access_key=None, session_token=None, target_platform=None, repodata_patch=None, write_zst=true, write_shards=true, force=false, max_parallel=None))] pub fn py_index_s3( py: Python<'_>, channel_url: String, - region: String, - endpoint_url: String, - force_path_style: bool, + region: Option, + endpoint_url: Option, + force_path_style: Option, access_key_id: Option, secret_access_key: Option, session_token: Option, @@ -59,7 +59,10 @@ pub fn py_index_s3( max_parallel: Option, ) -> PyResult> { let channel_url = Url::parse(&channel_url).map_err(PyRattlerError::from)?; - let endpoint_url = Url::parse(&endpoint_url).map_err(PyRattlerError::from)?; + let endpoint_url = match endpoint_url { + Some(url) => Some(Url::parse(&url).map_err(PyRattlerError::from)?), + None => None, + }; let target_platform = target_platform.map(Platform::from); future_into_py(py, async move { index_s3(IndexS3Config { From 32bb14abb8fd25f645e0be516414b20293898e89 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:20:45 +0200 Subject: [PATCH 09/45] test: add minio test --- .github/workflows/e2e-s3-tests.yml | 215 +++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 .github/workflows/e2e-s3-tests.yml diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml new file mode 100644 index 0000000000..2ea22f8871 --- /dev/null +++ b/.github/workflows/e2e-s3-tests.yml @@ -0,0 +1,215 @@ +on: + push: + branches: [main] + pull_request: + paths: + - crates/rattler-bin/** + - crates/rattler_index/** + - crates/rattler_upload/** + - crates/rattler_networking/** + - .github/workflows/e2e-s3-tests.yml + +name: E2E S3 Tests + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + RUST_LOG: info + RUST_BACKTRACE: 1 + RUSTFLAGS: "-D warnings" + CARGO_TERM_COLOR: always + DEFAULT_FEATURES: indicatif,tokio,serde,reqwest,sparse,gateway,resolvo,libsolv_c,s3,experimental_extras,edit,rattler_config + +jobs: + e2e-minio-test: + name: E2E Minio Upload/Index/Download Test + runs-on: ubuntu-latest + services: + minio: + image: minio/minio:RELEASE.2024-08-17T01-24-54Z + env: + MINIO_ACCESS_KEY: minioadmin + MINIO_SECRET_KEY: minioadmin + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + ports: + - "9000:9000" + - "9001:9001" + options: >- + --health-cmd "curl -f http://localhost:9000/minio/health/live" + --health-interval 30s + --health-timeout 20s + --health-retries 3 + volumes: + - /tmp/data:/data + command: server /data --console-address ":9001" + + steps: + - name: Checkout source code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + + - name: Set up pixi + uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 + + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + + - name: Build rattler binaries + run: | + pixi run build --bin rattler --release + pixi run build --bin rattler-index --release + + - name: Install Minio client + run: | + curl -o /tmp/mc https://dl.min.io/client/mc/release/linux-amd64/mc + chmod +x /tmp/mc + sudo mv /tmp/mc /usr/local/bin/mc + + - name: Wait for Minio to be ready + run: | + timeout 60 bash -c 'until curl -f http://localhost:9000/minio/health/live; do sleep 2; done' + + - name: Setup Minio bucket and policies + env: + MC_HOST_minio: http://minioadmin:minioadmin@localhost:9000 + run: | + mc alias set minio http://localhost:9000 minioadmin minioadmin + mc mb minio/test-channel || true + mc policy set public minio/test-channel + + - name: Run E2E S3 workflow test with Minio + run: | + set -e + + # Minio connection parameters + MINIO_ENDPOINT="http://localhost:9000" + MINIO_ACCESS_KEY="minioadmin" + MINIO_SECRET_KEY="minioadmin" + MINIO_REGION="us-east-1" + + echo "=== Step 1: Upload package to Minio ===" + ./target/release/rattler upload conda \ + --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ + --channel s3://test-channel \ + --aws-access-key-id "${MINIO_ACCESS_KEY}" \ + --aws-secret-access-key "${MINIO_SECRET_KEY}" \ + --aws-region "${MINIO_REGION}" \ + --endpoint-url "${MINIO_ENDPOINT}" + + echo "=== Step 2: Index the channel ===" + ./target/release/rattler-index \ + --channel s3://test-channel \ + --subdir noarch \ + --aws-access-key-id "${MINIO_ACCESS_KEY}" \ + --aws-secret-access-key "${MINIO_SECRET_KEY}" \ + --aws-region "${MINIO_REGION}" \ + --endpoint-url "${MINIO_ENDPOINT}" + + echo "=== Step 3: Test package discovery with dry-run ===" + ./target/release/rattler create \ + --dry-run \ + --channels s3://test-channel \ + empty==0.1.0 + + echo "=== E2E test completed successfully ===" + + - name: Debug bucket contents on failure + if: failure() + run: | + echo "=== Minio bucket contents ===" + mc ls --recursive minio/test-channel || true + + - name: Cleanup + if: always() + run: | + echo "Minio test cleanup completed" + + e2e-aws-s3-test: + name: E2E Real AWS S3 Upload/Index/Download Test + runs-on: ubuntu-latest + # Only run on main branch to avoid creating too many test buckets + if: github.ref == 'refs/heads/main' + permissions: + id-token: write + contents: read + + steps: + - name: Checkout source code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + + - name: Set up pixi + uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 + + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + + - name: Build rattler binaries + run: | + pixi run build --bin rattler --release + pixi run build --bin rattler-index --release + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 + with: + role-to-assume: ${{ vars.AWS_ROLE_ARN }} + aws-region: us-east-1 + role-session-name: rattler-e2e-test + + - name: Generate unique bucket name + id: bucket + run: | + BUCKET_NAME="rattler-e2e-test-$(date +%s)-$(echo $GITHUB_RUN_ID | tail -c 6)" + echo "name=${BUCKET_NAME}" >> $GITHUB_OUTPUT + echo "Using bucket: ${BUCKET_NAME}" + + - name: Create test S3 bucket + uses: aitorres/aws-s3-create-bucket@v1.2.0 + with: + bucket-name: ${{ steps.bucket.outputs.name }} + aws-region: us-east-1 + acl: private + + - name: Run E2E AWS S3 workflow test + run: | + set -e + + echo "=== Step 1: Upload package to AWS S3 ===" + ./target/release/rattler upload conda \ + --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ + --channel s3://${{ steps.bucket.outputs.name }} + + echo "=== Step 2: Index the channel ===" + ./target/release/rattler-index \ + --channel s3://${{ steps.bucket.outputs.name }} \ + --subdir noarch + + echo "=== Step 3: Test package discovery with dry-run ===" + ./target/release/rattler create \ + --dry-run \ + --channels s3://${{ steps.bucket.outputs.name }} \ + empty==0.1.0 + + echo "=== AWS S3 E2E test completed successfully ===" + + - name: Debug bucket contents on failure + if: failure() + run: | + echo "=== AWS S3 bucket contents ===" + aws s3 ls s3://${{ steps.bucket.outputs.name }} --recursive || true + + - name: Cleanup AWS S3 bucket + if: always() + run: | + # Remove all objects first + aws s3 rm s3://${{ steps.bucket.outputs.name }} --recursive || true + # Then delete the bucket + aws s3 rb s3://${{ steps.bucket.outputs.name }} || true + echo "AWS S3 test cleanup completed" \ No newline at end of file From ccfb9577f5a7670373d791068835d747018914fe Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:30:42 +0200 Subject: [PATCH 10/45] test: use github actions --- .github/workflows/e2e-s3-tests.yml | 52 ++++++++++++++++-------------- .gitignore | 3 ++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 2ea22f8871..e5fb5e9be9 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -137,7 +137,11 @@ jobs: permissions: id-token: write contents: read - + + env: + AWS_REGION: eu-west-1 + BUCKET: tmp-${{ github.repository_owner }}-${{ github.event.repository.name }}-${{ github.run_id }}-${{ github.run_attempt }} + steps: - name: Checkout source code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -155,28 +159,26 @@ jobs: run: | pixi run build --bin rattler --release pixi run build --bin rattler-index --release - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 + + - name: Configure AWS (OIDC) + uses: aws-actions/configure-aws-credentials@v4 with: - role-to-assume: ${{ vars.AWS_ROLE_ARN }} - aws-region: us-east-1 - role-session-name: rattler-e2e-test - + aws-region: ${{ env.AWS_REGION }} + role-to-assume: arn:aws:iam:::role/ + - name: Generate unique bucket name id: bucket run: | - BUCKET_NAME="rattler-e2e-test-$(date +%s)-$(echo $GITHUB_RUN_ID | tail -c 6)" - echo "name=${BUCKET_NAME}" >> $GITHUB_OUTPUT - echo "Using bucket: ${BUCKET_NAME}" - - - name: Create test S3 bucket - uses: aitorres/aws-s3-create-bucket@v1.2.0 - with: - bucket-name: ${{ steps.bucket.outputs.name }} - aws-region: us-east-1 - acl: private - + # Create a unique bucket name using repository owner, name, run ID, and attempt number + aws s3api create-bucket \ + --bucket "${BUCKET}" \ + --create-bucket-configuration LocationConstraint=${AWS_REGION} + + # 1-day auto-expire objects (safety net to ensure we dont retain any files) + aws s3api put-bucket-lifecycle-configuration --bucket "BUCKET" --lifecycle-configuration '{ + "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] + }' + - name: Run E2E AWS S3 workflow test run: | set -e @@ -184,17 +186,17 @@ jobs: echo "=== Step 1: Upload package to AWS S3 ===" ./target/release/rattler upload conda \ --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ - --channel s3://${{ steps.bucket.outputs.name }} + --channel s3://${BUCKET} echo "=== Step 2: Index the channel ===" ./target/release/rattler-index \ - --channel s3://${{ steps.bucket.outputs.name }} \ + --channel s3://${BUCKET} \ --subdir noarch echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ --dry-run \ - --channels s3://${{ steps.bucket.outputs.name }} \ + --channels s3://${BUCKET} \ empty==0.1.0 echo "=== AWS S3 E2E test completed successfully ===" @@ -203,13 +205,13 @@ jobs: if: failure() run: | echo "=== AWS S3 bucket contents ===" - aws s3 ls s3://${{ steps.bucket.outputs.name }} --recursive || true + aws s3 ls s3://${BUCKET} --recursive || true - name: Cleanup AWS S3 bucket if: always() run: | # Remove all objects first - aws s3 rm s3://${{ steps.bucket.outputs.name }} --recursive || true + aws s3 rm s3://${BUCKET} --recursive || true # Then delete the bucket - aws s3 rb s3://${{ steps.bucket.outputs.name }} || true + aws s3 rb s3://${BUCKET} || true echo "AWS S3 test cleanup completed" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2dc98f6528..54abac49a4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ test-data/channels/conda-forge/ # Visual studio files .vs/ + +# Local environment files +*.local.* \ No newline at end of file From a4a3f3240a46274cc5a3ca70791425646a8e6902 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:32:34 +0200 Subject: [PATCH 11/45] test: fix sha --- .github/workflows/e2e-s3-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index e5fb5e9be9..f82902bcb0 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -161,7 +161,7 @@ jobs: pixi run build --bin rattler-index --release - name: Configure AWS (OIDC) - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: aws-region: ${{ env.AWS_REGION }} role-to-assume: arn:aws:iam:::role/ From c4ecb715974a6604fa42345091d1169c8c1ee4f7 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:34:31 +0200 Subject: [PATCH 12/45] fix: precommit --- .github/workflows/e2e-s3-tests.yml | 44 +++++++++++++++--------------- .gitignore | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index f82902bcb0..ad8d57079e 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -45,35 +45,35 @@ jobs: volumes: - /tmp/data:/data command: server /data --console-address ":9001" - + steps: - name: Checkout source code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: submodules: recursive - + - name: Set up pixi uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 - + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 with: save-if: ${{ github.ref == 'refs/heads/main' }} - + - name: Build rattler binaries run: | pixi run build --bin rattler --release pixi run build --bin rattler-index --release - + - name: Install Minio client run: | curl -o /tmp/mc https://dl.min.io/client/mc/release/linux-amd64/mc chmod +x /tmp/mc sudo mv /tmp/mc /usr/local/bin/mc - + - name: Wait for Minio to be ready run: | timeout 60 bash -c 'until curl -f http://localhost:9000/minio/health/live; do sleep 2; done' - + - name: Setup Minio bucket and policies env: MC_HOST_minio: http://minioadmin:minioadmin@localhost:9000 @@ -81,17 +81,17 @@ jobs: mc alias set minio http://localhost:9000 minioadmin minioadmin mc mb minio/test-channel || true mc policy set public minio/test-channel - + - name: Run E2E S3 workflow test with Minio run: | set -e - + # Minio connection parameters MINIO_ENDPOINT="http://localhost:9000" MINIO_ACCESS_KEY="minioadmin" MINIO_SECRET_KEY="minioadmin" MINIO_REGION="us-east-1" - + echo "=== Step 1: Upload package to Minio ===" ./target/release/rattler upload conda \ --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ @@ -100,7 +100,7 @@ jobs: --aws-secret-access-key "${MINIO_SECRET_KEY}" \ --aws-region "${MINIO_REGION}" \ --endpoint-url "${MINIO_ENDPOINT}" - + echo "=== Step 2: Index the channel ===" ./target/release/rattler-index \ --channel s3://test-channel \ @@ -109,13 +109,13 @@ jobs: --aws-secret-access-key "${MINIO_SECRET_KEY}" \ --aws-region "${MINIO_REGION}" \ --endpoint-url "${MINIO_ENDPOINT}" - + echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ --dry-run \ --channels s3://test-channel \ empty==0.1.0 - + echo "=== E2E test completed successfully ===" - name: Debug bucket contents on failure @@ -147,14 +147,14 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: submodules: recursive - + - name: Set up pixi uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 - + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 with: save-if: ${{ github.ref == 'refs/heads/main' }} - + - name: Build rattler binaries run: | pixi run build --bin rattler --release @@ -173,7 +173,7 @@ jobs: aws s3api create-bucket \ --bucket "${BUCKET}" \ --create-bucket-configuration LocationConstraint=${AWS_REGION} - + # 1-day auto-expire objects (safety net to ensure we dont retain any files) aws s3api put-bucket-lifecycle-configuration --bucket "BUCKET" --lifecycle-configuration '{ "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] @@ -182,23 +182,23 @@ jobs: - name: Run E2E AWS S3 workflow test run: | set -e - + echo "=== Step 1: Upload package to AWS S3 ===" ./target/release/rattler upload conda \ --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ --channel s3://${BUCKET} - + echo "=== Step 2: Index the channel ===" ./target/release/rattler-index \ --channel s3://${BUCKET} \ --subdir noarch - + echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ --dry-run \ --channels s3://${BUCKET} \ empty==0.1.0 - + echo "=== AWS S3 E2E test completed successfully ===" - name: Debug bucket contents on failure @@ -214,4 +214,4 @@ jobs: aws s3 rm s3://${BUCKET} --recursive || true # Then delete the bucket aws s3 rb s3://${BUCKET} || true - echo "AWS S3 test cleanup completed" \ No newline at end of file + echo "AWS S3 test cleanup completed" diff --git a/.gitignore b/.gitignore index 54abac49a4..52b4f17068 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,4 @@ test-data/channels/conda-forge/ .vs/ # Local environment files -*.local.* \ No newline at end of file +*.local.* From 625338774786d064bbb2c053cd2ed0a4a443685b Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:42:20 +0200 Subject: [PATCH 13/45] simpler minio --- .github/workflows/e2e-s3-tests.yml | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index ad8d57079e..ad1a034afa 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -20,7 +20,7 @@ env: RUST_BACKTRACE: 1 RUSTFLAGS: "-D warnings" CARGO_TERM_COLOR: always - DEFAULT_FEATURES: indicatif,tokio,serde,reqwest,sparse,gateway,resolvo,libsolv_c,s3,experimental_extras,edit,rattler_config + DEFAULT_FEATURES: s3 jobs: e2e-minio-test: @@ -28,23 +28,18 @@ jobs: runs-on: ubuntu-latest services: minio: - image: minio/minio:RELEASE.2024-08-17T01-24-54Z - env: - MINIO_ACCESS_KEY: minioadmin - MINIO_SECRET_KEY: minioadmin - MINIO_ROOT_USER: minioadmin - MINIO_ROOT_PASSWORD: minioadmin + image: minio/minio:latest ports: - - "9000:9000" - - "9001:9001" + - 9000:9000 + env: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: minio123 options: >- - --health-cmd "curl -f http://localhost:9000/minio/health/live" - --health-interval 30s - --health-timeout 20s - --health-retries 3 - volumes: - - /tmp/data:/data - command: server /data --console-address ":9001" + --health-cmd "curl -f http://localhost:9000/minio/health/live || exit 1" + --health-interval 5s + --health-timeout 5s + --health-retries 5 + minio server /data steps: - name: Checkout source code From c6107fc743f5963219542dc8c943c3470a97b097 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:55:15 +0200 Subject: [PATCH 14/45] simpler minio --- .github/workflows/e2e-s3-tests.yml | 87 +++++++++++++++++------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index ad1a034afa..d5b101906f 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -26,20 +26,11 @@ jobs: e2e-minio-test: name: E2E Minio Upload/Index/Download Test runs-on: ubuntu-latest - services: - minio: - image: minio/minio:latest - ports: - - 9000:9000 - env: - MINIO_ROOT_USER: minio - MINIO_ROOT_PASSWORD: minio123 - options: >- - --health-cmd "curl -f http://localhost:9000/minio/health/live || exit 1" - --health-interval 5s - --health-timeout 5s - --health-retries 5 - minio server /data + env: + MINIO_ENDPOINT: http://localhost:9000 + MINIO_ACCESS_KEY: minio + MINIO_SECRET_KEY: minio123 + MINIO_REGION: us-east-1 steps: - name: Checkout source code @@ -47,6 +38,38 @@ jobs: with: submodules: recursive + - name: Install MinIO + mc, start server, configure bucket + run: | + set -euo pipefail + + # Paths + BIN_DIR="$RUNNER_TEMP" + DATA_DIR="$RUNNER_TEMP/minio-data" + LOG_FILE="$RUNNER_TEMP/minio.log" + PID_FILE="$RUNNER_TEMP/minio.pid" + + mkdir -p "$BIN_DIR" "$DATA_DIR" + + # Download MinIO server and client without sudo; add to PATH + curl -fsSL -o "$BIN_DIR/minio" https://dl.min.io/server/minio/release/linux-amd64/minio + curl -fsSL -o "$BIN_DIR/mc" https://dl.min.io/client/mc/release/linux-amd64/mc + chmod +x "$BIN_DIR/minio" "$BIN_DIR/mc" + echo "$BIN_DIR" >> "$GITHUB_PATH" + + # Start MinIO in background + MINIO_ROOT_USER="$MINIO_ACCESS_KEY" \ + MINIO_ROOT_PASSWORD="$MINIO_SECRET_KEY" \ + "$BIN_DIR/minio" server "$DATA_DIR" --address ":9000" > "$LOG_FILE" 2>&1 & + echo $! > "$PID_FILE" + + # Wait for MinIO to be ready + timeout 120 bash -c 'until curl -fsS http://localhost:9000/minio/health/live >/dev/null; do sleep 2; done' + + # Configure mc client and bucket + "$BIN_DIR/mc" alias set minio http://localhost:9000 "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" + "$BIN_DIR/mc" mb minio/test-channel || true + "$BIN_DIR/mc" policy set download minio/test-channel + - name: Set up pixi uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 @@ -59,34 +82,12 @@ jobs: pixi run build --bin rattler --release pixi run build --bin rattler-index --release - - name: Install Minio client - run: | - curl -o /tmp/mc https://dl.min.io/client/mc/release/linux-amd64/mc - chmod +x /tmp/mc - sudo mv /tmp/mc /usr/local/bin/mc - - - name: Wait for Minio to be ready - run: | - timeout 60 bash -c 'until curl -f http://localhost:9000/minio/health/live; do sleep 2; done' - - - name: Setup Minio bucket and policies - env: - MC_HOST_minio: http://minioadmin:minioadmin@localhost:9000 - run: | - mc alias set minio http://localhost:9000 minioadmin minioadmin - mc mb minio/test-channel || true - mc policy set public minio/test-channel + # MinIO client and server are installed and configured above - name: Run E2E S3 workflow test with Minio run: | set -e - # Minio connection parameters - MINIO_ENDPOINT="http://localhost:9000" - MINIO_ACCESS_KEY="minioadmin" - MINIO_SECRET_KEY="minioadmin" - MINIO_REGION="us-east-1" - echo "=== Step 1: Upload package to Minio ===" ./target/release/rattler upload conda \ --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ @@ -122,6 +123,18 @@ jobs: - name: Cleanup if: always() run: | + # Stop Minio server (pid written in RUNNER_TEMP) + if [ -f "$RUNNER_TEMP/minio.pid" ]; then + kill "$(cat "$RUNNER_TEMP/minio.pid")" || true + rm -f "$RUNNER_TEMP/minio.pid" + fi + + # Show Minio logs for debugging + if [ -f "$RUNNER_TEMP/minio.log" ]; then + echo "=== Minio server logs ===" + tail -20 "$RUNNER_TEMP/minio.log" || true + fi + echo "Minio test cleanup completed" e2e-aws-s3-test: From 1d4314eb22ace55dd1b3c2f3ac55dd2fed72a365 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:04:41 +0200 Subject: [PATCH 15/45] dont use pixi --- .github/workflows/e2e-s3-tests.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index d5b101906f..a65aca08fb 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -70,17 +70,14 @@ jobs: "$BIN_DIR/mc" mb minio/test-channel || true "$BIN_DIR/mc" policy set download minio/test-channel - - name: Set up pixi - uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 with: save-if: ${{ github.ref == 'refs/heads/main' }} - name: Build rattler binaries run: | - pixi run build --bin rattler --release - pixi run build --bin rattler-index --release + cargo build --bin rattler --release + cargo build --bin rattler-index --release # MinIO client and server are installed and configured above @@ -156,17 +153,14 @@ jobs: with: submodules: recursive - - name: Set up pixi - uses: prefix-dev/setup-pixi@8ca4608ef7f4daeb54f5205b20d0b7cb42f11143 # v0.8.14 - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 with: save-if: ${{ github.ref == 'refs/heads/main' }} - name: Build rattler binaries run: | - pixi run build --bin rattler --release - pixi run build --bin rattler-index --release + cargo build --bin rattler --release + cargo build --bin rattler-index --release - name: Configure AWS (OIDC) uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 From 0e3bf676a3845134ff6405b332f8e1205eadb0ed Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:15:48 +0200 Subject: [PATCH 16/45] fix: cli args --- .github/workflows/e2e-s3-tests.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index a65aca08fb..92414e3be0 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -86,21 +86,20 @@ jobs: set -e echo "=== Step 1: Upload package to Minio ===" - ./target/release/rattler upload conda \ - --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ + ./target/release/rattler upload s3 \ --channel s3://test-channel \ - --aws-access-key-id "${MINIO_ACCESS_KEY}" \ - --aws-secret-access-key "${MINIO_SECRET_KEY}" \ - --aws-region "${MINIO_REGION}" \ - --endpoint-url "${MINIO_ENDPOINT}" + --access-key-id "${MINIO_ACCESS_KEY}" \ + --secret-access-key "${MINIO_SECRET_KEY}" \ + --region "${MINIO_REGION}" \ + --endpoint-url "${MINIO_ENDPOINT}" \ + test-data/packages/empty-0.1.0-h4616a5c_0.conda echo "=== Step 2: Index the channel ===" - ./target/release/rattler-index \ - --channel s3://test-channel \ - --subdir noarch \ - --aws-access-key-id "${MINIO_ACCESS_KEY}" \ - --aws-secret-access-key "${MINIO_SECRET_KEY}" \ - --aws-region "${MINIO_REGION}" \ + ./target/release/rattler-index s3 \ + s3://test-channel \ + --access-key-id "${MINIO_ACCESS_KEY}" \ + --secret-access-key "${MINIO_SECRET_KEY}" \ + --region "${MINIO_REGION}" \ --endpoint-url "${MINIO_ENDPOINT}" echo "=== Step 3: Test package discovery with dry-run ===" From 061f63b176892cd997d4436ccb93b539d2f3ae90 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:42:02 +0200 Subject: [PATCH 17/45] fix: setup role --- .github/workflows/e2e-s3-tests.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 92414e3be0..a12e4b11c5 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -92,6 +92,7 @@ jobs: --secret-access-key "${MINIO_SECRET_KEY}" \ --region "${MINIO_REGION}" \ --endpoint-url "${MINIO_ENDPOINT}" \ + --force-path-style true \ test-data/packages/empty-0.1.0-h4616a5c_0.conda echo "=== Step 2: Index the channel ===" @@ -100,7 +101,8 @@ jobs: --access-key-id "${MINIO_ACCESS_KEY}" \ --secret-access-key "${MINIO_SECRET_KEY}" \ --region "${MINIO_REGION}" \ - --endpoint-url "${MINIO_ENDPOINT}" + --endpoint-url "${MINIO_ENDPOINT}" \ + --force-path-style true echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ @@ -165,7 +167,7 @@ jobs: uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: aws-region: ${{ env.AWS_REGION }} - role-to-assume: arn:aws:iam:::role/ + role-to-assume: arn:aws:iam::239378270001:role/conda-rattler-e2e-test - name: Generate unique bucket name id: bucket @@ -185,14 +187,12 @@ jobs: set -e echo "=== Step 1: Upload package to AWS S3 ===" - ./target/release/rattler upload conda \ - --package test-data/packages/empty-0.1.0-h4616a5c_0.conda \ - --channel s3://${BUCKET} + ./target/release/rattler upload s3 \ + --channel s3://${BUCKET} \ + test-data/packages/empty-0.1.0-h4616a5c_0.conda echo "=== Step 2: Index the channel ===" - ./target/release/rattler-index \ - --channel s3://${BUCKET} \ - --subdir noarch + ./target/release/rattler-index s3 s3://${BUCKET} echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ From 903b99a74beb24f22de53412ea534d14466a29bd Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:47:30 +0200 Subject: [PATCH 18/45] fix: enable ss3 --- .github/workflows/e2e-s3-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index a12e4b11c5..abaa8c112b 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -139,7 +139,7 @@ jobs: name: E2E Real AWS S3 Upload/Index/Download Test runs-on: ubuntu-latest # Only run on main branch to avoid creating too many test buckets - if: github.ref == 'refs/heads/main' + # if: github.ref == 'refs/heads/main' permissions: id-token: write contents: read From 413342c036f9db3ede445b0a438cae4967c9941c Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:13:02 +0200 Subject: [PATCH 19/45] fix: s3 --- .github/workflows/e2e-s3-tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index abaa8c112b..6c62cb089b 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -107,7 +107,7 @@ jobs: echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ --dry-run \ - --channels s3://test-channel \ + -c s3://test-channel \ empty==0.1.0 echo "=== E2E test completed successfully ===" @@ -158,11 +158,6 @@ jobs: with: save-if: ${{ github.ref == 'refs/heads/main' }} - - name: Build rattler binaries - run: | - cargo build --bin rattler --release - cargo build --bin rattler-index --release - - name: Configure AWS (OIDC) uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: @@ -181,6 +176,11 @@ jobs: aws s3api put-bucket-lifecycle-configuration --bucket "BUCKET" --lifecycle-configuration '{ "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] }' + + - name: Build rattler binaries + run: | + cargo build --bin rattler --release + cargo build --bin rattler-index --release - name: Run E2E AWS S3 workflow test run: | @@ -197,7 +197,7 @@ jobs: echo "=== Step 3: Test package discovery with dry-run ===" ./target/release/rattler create \ --dry-run \ - --channels s3://${BUCKET} \ + -c s3://${BUCKET} \ empty==0.1.0 echo "=== AWS S3 E2E test completed successfully ===" From e4c7f008a43d0e6956ba6d9ca66bb2c75b7839f4 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:16:16 +0200 Subject: [PATCH 20/45] fix: s3 bucket name --- .github/workflows/e2e-s3-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 6c62cb089b..7d673b2ae2 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -173,7 +173,7 @@ jobs: --create-bucket-configuration LocationConstraint=${AWS_REGION} # 1-day auto-expire objects (safety net to ensure we dont retain any files) - aws s3api put-bucket-lifecycle-configuration --bucket "BUCKET" --lifecycle-configuration '{ + aws s3api put-bucket-lifecycle-configuration --bucket "${BUCKET}" --lifecycle-configuration '{ "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] }' From 476d2529d00cd6772a1de4a96cddebbb654837e6 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:46:00 +0200 Subject: [PATCH 21/45] test: use pixi for e2e minio test --- .github/workflows/e2e-s3-tests.yml | 103 +- Cargo.lock | 2 + crates/rattler-bin/Cargo.toml | 2 + crates/rattler-bin/pixi.toml | 11 + crates/rattler-bin/src/commands/auth.rs | 3 +- crates/rattler_index/pixi.lock | 8 + crates/rattler_index/pixi.toml | 10 + crates/rattler_upload/Cargo.toml | 2 +- pixi.lock | 2030 +++++++++++++++++++++++ pixi.toml | 35 +- scripts/e2e/s3-minio.nu | 85 + 11 files changed, 2190 insertions(+), 101 deletions(-) create mode 100644 crates/rattler-bin/pixi.toml create mode 100644 crates/rattler_index/pixi.lock create mode 100644 crates/rattler_index/pixi.toml create mode 100644 scripts/e2e/s3-minio.nu diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 4208c48b5d..67faa4e9b2 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -26,11 +26,6 @@ jobs: e2e-minio-test: name: E2E Upload/Index/Download [Minio] runs-on: ubuntu-latest - env: - MINIO_ENDPOINT: http://localhost:9000 - MINIO_ACCESS_KEY: minio - MINIO_SECRET_KEY: minio123 - MINIO_REGION: us-east-1 steps: - name: Checkout source code @@ -38,102 +33,14 @@ jobs: with: submodules: recursive - - name: Install MinIO + mc, start server, configure bucket - run: | - set -euo pipefail - - # Paths - BIN_DIR="$RUNNER_TEMP" - DATA_DIR="$RUNNER_TEMP/minio-data" - LOG_FILE="$RUNNER_TEMP/minio.log" - PID_FILE="$RUNNER_TEMP/minio.pid" - - mkdir -p "$BIN_DIR" "$DATA_DIR" - - # Download MinIO server and client without sudo; add to PATH - curl -fsSL -o "$BIN_DIR/minio" https://dl.min.io/server/minio/release/linux-amd64/minio - curl -fsSL -o "$BIN_DIR/mc" https://dl.min.io/client/mc/release/linux-amd64/mc - chmod +x "$BIN_DIR/minio" "$BIN_DIR/mc" - echo "$BIN_DIR" >> "$GITHUB_PATH" - - # Start MinIO in background - MINIO_ROOT_USER="$MINIO_ACCESS_KEY" \ - MINIO_ROOT_PASSWORD="$MINIO_SECRET_KEY" \ - "$BIN_DIR/minio" server "$DATA_DIR" --address ":9000" > "$LOG_FILE" 2>&1 & - echo $! > "$PID_FILE" - - # Wait for MinIO to be ready - timeout 120 bash -c 'until curl -fsS http://localhost:9000/minio/health/live >/dev/null; do sleep 2; done' - - # Configure mc client and bucket - "$BIN_DIR/mc" alias set minio http://localhost:9000 "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" - "$BIN_DIR/mc" mb minio/test-channel || true - "$BIN_DIR/mc" policy set download minio/test-channel + - uses: mozilla-actions/sccache-action@v0.0.9 - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + - uses: prefix-dev/setup-pixi@v0.9.0 with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - - name: Build rattler binaries - run: | - cargo build --bin rattler --release - cargo build --bin rattler-index --release - - # MinIO client and server are installed and configured above - - - name: Run E2E S3 workflow test with Minio - run: | - set -e + environments: minio + activate-environment: minio - echo "=== Step 1: Upload package to Minio ===" - ./target/release/rattler upload s3 \ - --channel s3://test-channel \ - --access-key-id "${MINIO_ACCESS_KEY}" \ - --secret-access-key "${MINIO_SECRET_KEY}" \ - --region "${MINIO_REGION}" \ - --endpoint-url "${MINIO_ENDPOINT}" \ - --force-path-style true \ - test-data/packages/empty-0.1.0-h4616a5c_0.conda - - echo "=== Step 2: Index the channel ===" - ./target/release/rattler-index s3 \ - s3://test-channel \ - --access-key-id "${MINIO_ACCESS_KEY}" \ - --secret-access-key "${MINIO_SECRET_KEY}" \ - --region "${MINIO_REGION}" \ - --endpoint-url "${MINIO_ENDPOINT}" \ - --force-path-style true - - echo "=== Step 3: Test package discovery with dry-run ===" - ./target/release/rattler create \ - --dry-run \ - -c s3://test-channel \ - empty==0.1.0 - - echo "=== E2E test completed successfully ===" - - - name: Debug bucket contents on failure - if: failure() - run: | - echo "=== Minio bucket contents ===" - mc ls --recursive minio/test-channel || true - - - name: Cleanup - if: always() - run: | - # Stop Minio server (pid written in RUNNER_TEMP) - if [ -f "$RUNNER_TEMP/minio.pid" ]; then - kill "$(cat "$RUNNER_TEMP/minio.pid")" || true - rm -f "$RUNNER_TEMP/minio.pid" - fi - - # Show Minio logs for debugging - if [ -f "$RUNNER_TEMP/minio.log" ]; then - echo "=== Minio server logs ===" - tail -20 "$RUNNER_TEMP/minio.log" || true - fi - - echo "Minio test cleanup completed" + - run: pixi run e2e-s3-minio e2e-aws-s3-test: name: E2E Upload/Index/Download [AWS S3] diff --git a/Cargo.lock b/Cargo.lock index 537820fcf5..fc90532b0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4485,6 +4485,7 @@ dependencies = [ "console 0.16.0", "indicatif", "itertools 0.14.0", + "miette", "once_cell", "rattler", "rattler_cache", @@ -4493,6 +4494,7 @@ dependencies = [ "rattler_networking", "rattler_repodata_gateway", "rattler_solve", + "rattler_upload", "rattler_virtual_packages", "reqwest", "reqwest-middleware", diff --git a/crates/rattler-bin/Cargo.toml b/crates/rattler-bin/Cargo.toml index 3be8aa78e2..57a15fa72c 100644 --- a/crates/rattler-bin/Cargo.toml +++ b/crates/rattler-bin/Cargo.toml @@ -26,6 +26,7 @@ anyhow = { workspace = true } clap = { workspace = true, features = ["derive"] } console = { workspace = true, features = ["windows-console-colors"] } indicatif = { workspace = true } +miette = { workspace = true } once_cell = { workspace = true } rattler = { workspace = true, features = ["indicatif", "cli-tools"] } rattler_conda_types = { workspace = true, default-features = false } @@ -35,6 +36,7 @@ rattler_solve = { workspace = true, default-features = false, features = ["resol rattler_virtual_packages = { workspace = true, default-features = false } rattler_cache = { workspace = true, default-features = false } rattler_menuinst = { workspace = true, default-features = false } +rattler_upload = { workspace = true, features = ["s3"]} reqwest = { workspace = true, features = ["stream"] } reqwest-middleware = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/crates/rattler-bin/pixi.toml b/crates/rattler-bin/pixi.toml new file mode 100644 index 0000000000..fb498d9d6e --- /dev/null +++ b/crates/rattler-bin/pixi.toml @@ -0,0 +1,11 @@ +[package] +name = "rattler" +version = "0.1.0" + +[package.build.backend] +name = "pixi-build-rust" +version = "0.3.*" +channels = [ + "https://prefix.dev/pixi-build-backends", + "https://prefix.dev/conda-forge" +] \ No newline at end of file diff --git a/crates/rattler-bin/src/commands/auth.rs b/crates/rattler-bin/src/commands/auth.rs index 1de4e19d1a..14a64077f1 100644 --- a/crates/rattler-bin/src/commands/auth.rs +++ b/crates/rattler-bin/src/commands/auth.rs @@ -1,7 +1,8 @@ +use miette::IntoDiagnostic; use rattler::cli::auth; pub type Opt = auth::Args; pub async fn auth(opt: Opt) -> miette::Result<()> { - auth::execute(opt).await.map_err(|e| anyhow::anyhow!(e)) + auth::execute(opt).await.into_diagnostic() } diff --git a/crates/rattler_index/pixi.lock b/crates/rattler_index/pixi.lock new file mode 100644 index 0000000000..fd07ef5c91 --- /dev/null +++ b/crates/rattler_index/pixi.lock @@ -0,0 +1,8 @@ +version: 6 +environments: + default: + channels: + - url: https://prefix.dev/pixi-build-backends/ + - url: https://prefix.dev/conda-forge/ + packages: {} +packages: [] diff --git a/crates/rattler_index/pixi.toml b/crates/rattler_index/pixi.toml new file mode 100644 index 0000000000..1b87d1e8e6 --- /dev/null +++ b/crates/rattler_index/pixi.toml @@ -0,0 +1,10 @@ +[package] +name = "rattler_index" + +[package.build.backend] +name = "pixi-build-rust" +version = "0.3.*" +channels = [ + "https://prefix.dev/pixi-build-backends", + "https://prefix.dev/conda-forge" +] \ No newline at end of file diff --git a/crates/rattler_upload/Cargo.toml b/crates/rattler_upload/Cargo.toml index f36c8bd403..dd9a47fd36 100644 --- a/crates/rattler_upload/Cargo.toml +++ b/crates/rattler_upload/Cargo.toml @@ -22,7 +22,7 @@ rattler_package_streaming = { workspace = true, default-features = false } rattler_config = { workspace = true, default-features = false } rattler_solve = { workspace = true } miette = { workspace = true, features = ["fancy"] } -clap = { workspace = true } +clap = { workspace = true, features = ["env"] } fs-err = { workspace = true, features = ["tokio"] } futures = { workspace = true } indicatif = { workspace = true } diff --git a/pixi.lock b/pixi.lock index d779eb0a21..069cc6ad4e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -385,6 +385,270 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/virtualenv-20.29.2-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_26.conda - conda: https://prefix.dev/conda-forge/win-64/yaml-0.2.5-h8ffe710_2.tar.bz2 + minio: + channels: + - url: https://prefix.dev/conda-forge/ + packages: + linux-64: + - conda: https://prefix.dev/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://prefix.dev/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/libgcc-15.1.0-h767d61c_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/libgomp-15.1.0-h767d61c_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://prefix.dev/conda-forge/linux-64/minio-client-2025.07.21.05.28.08-hfc2019e_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/minio-server-2025.01.20.14.49.07-hbcca054_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/nushell-0.106.1-hb0b18c3_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/openssl-3.5.2-h26f9b46_0.conda + - conda: crates/rattler-bin + subdir: linux-64 + - conda: crates/rattler_index + subdir: linux-64 + osx-64: + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/libcxx-20.1.8-h3d58e20_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda + - conda: https://prefix.dev/conda-forge/osx-64/minio-client-2025.07.21.05.28.08-hccc6df8_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/minio-server-2025.01.20.14.49.07-h8857fd0_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/nushell-0.106.1-h088d292_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/openssl-3.5.2-h6e31bce_0.conda + - conda: crates/rattler-bin + subdir: osx-64 + - conda: crates/rattler_index + subdir: osx-64 + osx-arm64: + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libcxx-20.1.8-hf598326_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/minio-client-2025.07.21.05.28.08-h820172f_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/minio-server-2025.01.20.14.49.07-hf0a4a13_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/nushell-0.106.1-h1b39ca6_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda + - conda: crates/rattler-bin + subdir: osx-arm64 + - conda: crates/rattler_index + subdir: osx-arm64 + win-64: + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-h4c7d964_0.conda + - conda: https://prefix.dev/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda + - conda: https://prefix.dev/conda-forge/win-64/minio-client-2025.07.21.05.28.08-h11686cb_0.conda + - conda: https://prefix.dev/conda-forge/win-64/minio-server-2025.01.20.14.49.07-h56e8100_1.conda + - conda: https://prefix.dev/conda-forge/win-64/nushell-0.106.1-hdab3696_0.conda + - conda: https://prefix.dev/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda + - conda: https://prefix.dev/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda + - conda: https://prefix.dev/conda-forge/win-64/vc-14.3-h41ae7f8_31.conda + - conda: https://prefix.dev/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda + - conda: https://prefix.dev/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda + - conda: crates/rattler-bin + subdir: win-64 + - conda: crates/rattler_index + subdir: win-64 + s3: + channels: + - url: https://prefix.dev/conda-forge/ + packages: + linux-64: + - conda: https://prefix.dev/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://prefix.dev/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-auth-0.9.0-h0fbd49f_19.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-cal-0.9.2-he7b75e1_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-common-0.12.4-hb03c661_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-compression-0.3.1-h92c474e_6.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-event-stream-0.5.5-h149bd38_3.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-http-0.10.4-h37a7233_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-io-0.21.2-h6252d9a_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-mqtt-0.13.3-h19deb91_3.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-s3-0.8.6-h800fcd2_2.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h92c474e_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/aws-checksums-0.2.7-h92c474e_2.conda + - conda: https://prefix.dev/conda-forge/linux-64/awscli-2.28.18-py313h78bf25f_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/awscrt-0.27.5-py313ha03020b_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/brotli-python-1.1.0-py313h46c70d0_3.conda + - conda: https://prefix.dev/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://prefix.dev/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/noarch/distro-1.8.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/docutils-0.18.1-py313h78bf25f_1.conda + - conda: https://prefix.dev/conda-forge/noarch/jmespath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/libgcc-15.1.0-h767d61c_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/libgomp-15.1.0-h767d61c_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://prefix.dev/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_4.conda + - conda: https://prefix.dev/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://prefix.dev/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://prefix.dev/conda-forge/linux-64/openssl-3.5.2-h26f9b46_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://prefix.dev/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda + - conda: https://prefix.dev/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://prefix.dev/conda-forge/linux-64/ruamel.yaml-0.17.17-py313h536fd9c_3.conda + - conda: https://prefix.dev/conda-forge/linux-64/ruamel.yaml.clib-0.2.8-py313h536fd9c_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/s2n-1.5.23-h8e187f5_0.conda + - conda: https://prefix.dev/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://prefix.dev/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda + - conda: https://prefix.dev/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://prefix.dev/conda-forge/noarch/urllib3-1.26.19-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: crates/rattler-bin + subdir: linux-64 + - conda: crates/rattler_index + subdir: linux-64 + osx-64: + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-auth-0.9.0-h9972aa3_19.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-cal-0.9.2-h6f29d6d_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-common-0.12.4-h1c43f85_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-compression-0.3.1-h7a4e982_6.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-event-stream-0.5.5-hf5ae603_3.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-http-0.10.4-hb3df2dd_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-io-0.21.2-h46f635e_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-mqtt-0.13.3-h6fbeeec_3.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-s3-0.8.6-he7aa9d9_2.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h7a4e982_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/aws-checksums-0.2.7-h7a4e982_2.conda + - conda: https://prefix.dev/conda-forge/osx-64/awscli-2.28.18-py312hb401068_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/awscrt-0.27.5-py312hef7181d_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/brotli-python-1.1.0-py312haafddd8_3.conda + - conda: https://prefix.dev/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://prefix.dev/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/noarch/distro-1.8.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/docutils-0.18.1-py312hb401068_1.conda + - conda: https://prefix.dev/conda-forge/noarch/jmespath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/libcxx-20.1.8-h3d58e20_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/libexpat-2.7.1-h21dd04a_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/libffi-3.4.6-h281671d_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda + - conda: https://prefix.dev/conda-forge/osx-64/libsqlite-3.50.4-h39a8b3b_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda + - conda: https://prefix.dev/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda + - conda: https://prefix.dev/conda-forge/osx-64/openssl-3.5.2-h6e31bce_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://prefix.dev/conda-forge/osx-64/python-3.12.11-h9ccd52b_0_cpython.conda + - conda: https://prefix.dev/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://prefix.dev/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda + - conda: https://prefix.dev/conda-forge/osx-64/ruamel.yaml-0.17.17-py312h3d0f464_3.conda + - conda: https://prefix.dev/conda-forge/osx-64/ruamel.yaml.clib-0.2.8-py312h3d0f464_1.conda + - conda: https://prefix.dev/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://prefix.dev/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-hf689a15_2.conda + - conda: https://prefix.dev/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://prefix.dev/conda-forge/noarch/urllib3-1.26.19-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: crates/rattler-bin + subdir: osx-64 + - conda: crates/rattler_index + subdir: osx-64 + osx-arm64: + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-auth-0.9.0-h9eee66f_19.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-cal-0.9.2-hd08b81e_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-common-0.12.4-h6caf38d_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-compression-0.3.1-habbe1e8_6.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-event-stream-0.5.5-hd1b68e1_3.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-http-0.10.4-h09a8a51_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-io-0.21.2-hc6344be_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-mqtt-0.13.3-h625c29d_3.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-s3-0.8.6-h6ded10d_2.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-habbe1e8_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/aws-checksums-0.2.7-habbe1e8_2.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/awscli-2.28.18-py313h8f79df9_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/awscrt-0.27.5-py313h3c677f0_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/brotli-python-1.1.0-py313h928ef07_3.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + - conda: https://prefix.dev/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/noarch/distro-1.8.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/docutils-0.18.1-py313h8f79df9_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://prefix.dev/conda-forge/noarch/jmespath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libcxx-20.1.8-hf598326_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libexpat-2.7.1-hec049ff_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.13.5-hf3f3da0_102_cp313.conda + - conda: https://prefix.dev/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/ruamel.yaml-0.17.17-py313h63a2874_3.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.8-py313h63a2874_1.conda + - conda: https://prefix.dev/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://prefix.dev/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + - conda: https://prefix.dev/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://prefix.dev/conda-forge/noarch/urllib3-1.26.19-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: crates/rattler-bin + subdir: osx-arm64 + - conda: crates/rattler_index + subdir: osx-arm64 + win-64: + - conda: https://prefix.dev/conda-forge/win-64/aws-c-auth-0.9.0-hd9a66b3_19.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-cal-0.9.2-hef2a5b8_1.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-common-0.12.4-hfd05255_0.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-compression-0.3.1-ha8a2810_6.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-event-stream-0.5.5-hccb7587_3.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-http-0.10.4-h04b3cea_0.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-io-0.21.2-h20b9e97_1.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-mqtt-0.13.3-h6b158f5_3.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-s3-0.8.6-h46905be_2.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-c-sdkutils-0.2.4-ha8a2810_1.conda + - conda: https://prefix.dev/conda-forge/win-64/aws-checksums-0.2.7-ha8a2810_2.conda + - conda: https://prefix.dev/conda-forge/win-64/awscli-2.28.18-py312h2e8e312_0.conda + - conda: https://prefix.dev/conda-forge/win-64/awscrt-0.27.5-py312h44c0e62_0.conda + - conda: https://prefix.dev/conda-forge/win-64/brotli-python-1.1.0-py312h275cf98_3.conda + - conda: https://prefix.dev/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda + - conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-h4c7d964_0.conda + - conda: https://prefix.dev/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/noarch/distro-1.8.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/win-64/docutils-0.18.1-py312h2e8e312_1.conda + - conda: https://prefix.dev/conda-forge/noarch/jmespath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda + - conda: https://prefix.dev/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda + - conda: https://prefix.dev/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda + - conda: https://prefix.dev/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda + - conda: https://prefix.dev/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda + - conda: https://prefix.dev/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda + - conda: https://prefix.dev/conda-forge/win-64/python-3.12.11-h3f84c4b_0_cpython.conda + - conda: https://prefix.dev/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://prefix.dev/conda-forge/win-64/ruamel.yaml-0.17.17-py312h4389bb4_3.conda + - conda: https://prefix.dev/conda-forge/win-64/ruamel.yaml.clib-0.2.8-py312h4389bb4_1.conda + - conda: https://prefix.dev/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://prefix.dev/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://prefix.dev/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda + - conda: https://prefix.dev/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://prefix.dev/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda + - conda: https://prefix.dev/conda-forge/noarch/urllib3-1.26.19-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/win-64/vc-14.3-h41ae7f8_31.conda + - conda: https://prefix.dev/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda + - conda: https://prefix.dev/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda + - conda: https://prefix.dev/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: https://prefix.dev/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda + - conda: crates/rattler-bin + subdir: win-64 + - conda: crates/rattler_index + subdir: win-64 packages: - conda: https://prefix.dev/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 @@ -419,6 +683,742 @@ packages: license_family: BSD size: 49468 timestamp: 1718213032772 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-auth-0.9.0-h0fbd49f_19.conda + sha256: 02bb7d2b21f60591944d97c2299be53c9c799085d0a1fb15620d5114cf161c3a + md5: 24139f2990e92effbeb374a0eb33fdb1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 122970 + timestamp: 1753305744902 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-auth-0.9.0-h9972aa3_19.conda + sha256: 386743f3dcfac108bcbb5d1c7e444ca8218284853615a8718a9092d4d71f0a1b + md5: 38551fbfe76020ffd06b3d77889d01f5 + depends: + - __osx >=10.13 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 110717 + timestamp: 1753305752177 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-auth-0.9.0-h9eee66f_19.conda + sha256: 743df69276ea22058299cc028a6bcb2a4bd172ba08de48c702baf4d49fb61c45 + md5: 7b554506535c66852c5090a14801dfb9 + depends: + - __osx >=11.0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 106630 + timestamp: 1753305735994 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-auth-0.9.0-hd9a66b3_19.conda + sha256: d38536adcc9b2907381e0f12cf9f92a831d5991819329d9bf93bcc5dd226417d + md5: 6bed5e0b1d39b4e99598112aff67b968 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 115951 + timestamp: 1753305747891 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-cal-0.9.2-he7b75e1_1.conda + sha256: 30ecca069fdae0aa6a8bb64c47eb5a8d9a7bef7316181e8cbb08b7cb47d8b20f + md5: c04d1312e7feec369308d656c18e7f3e + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - libgcc >=14 + - openssl >=3.5.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 50942 + timestamp: 1752240577225 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-cal-0.9.2-h6f29d6d_1.conda + sha256: 41d60e59a6c906636a6c82b441d10d21a1623fd03188965319572a17e03f4da1 + md5: 44f3a90d7c5a280f68bf1a7614f057b6 + depends: + - __osx >=10.13 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: Apache + size: 40872 + timestamp: 1752240723936 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-cal-0.9.2-hd08b81e_1.conda + sha256: 0cff81daf70f64bb8bf51f0883727d253c0462085f6bfe3d6c619479fbaec329 + md5: f8d75a83ced3f7296fed525502eac257 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: Apache + size: 41154 + timestamp: 1752240791193 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-cal-0.9.2-hef2a5b8_1.conda + sha256: cd396607f5ffdbdae6995ea135904f6efe7eaac19346aec07359684424819a16 + md5: 096193e01d32724a835517034a6926a2 + depends: + - aws-c-common >=0.12.4,<0.12.5.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + size: 49125 + timestamp: 1752241167516 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-common-0.12.4-hb03c661_0.conda + sha256: 6c9e1b9e82750c39ac0251dcfbeebcbb00a1af07c0d7e3fb1153c4920da316eb + md5: ae5621814cb99642c9308977fe90ed0d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + size: 236420 + timestamp: 1752193614294 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-common-0.12.4-h1c43f85_0.conda + sha256: 94e26ee718358b505aa3c3ddfcedcabd0882de9ff877057985151874b54e9851 + md5: f9547dfb10c15476c17d2d54b61747b8 + depends: + - __osx >=10.13 + license: Apache-2.0 + license_family: Apache + size: 228243 + timestamp: 1752193906883 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-common-0.12.4-h6caf38d_0.conda + sha256: d94c508308340b5b8294d2c382737b72b77e9df688610fa034d0a009a9339d73 + md5: 7a3edd3d065687fe3aa9a04a515fd2bf + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221313 + timestamp: 1752193769784 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-common-0.12.4-hfd05255_0.conda + sha256: c818a09c4d9fe228bb6c94a02c0b05f880ead16ca9f0f59675ae862479ea631a + md5: dcac61b0681b4a2c8e74772415f9e490 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + size: 235039 + timestamp: 1752193765837 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-compression-0.3.1-h92c474e_6.conda + sha256: 154d4a699f4d8060b7f2cec497a06e601cbd5c8cde6736ced0fb7e161bc6f1bb + md5: 3490e744cb8b9d5a3b9785839d618a17 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 22116 + timestamp: 1752240005329 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-compression-0.3.1-h7a4e982_6.conda + sha256: 2029ee55f83e1952ea0c220b0dd30f1b6f9e9411146c659489fcfd6a29af2ddf + md5: 6a4b25acf73532bbec863c2c2ae45842 + depends: + - __osx >=10.13 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 21116 + timestamp: 1752240021842 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-compression-0.3.1-habbe1e8_6.conda + sha256: 633c7ee0e80c24fa6354b2e1c940af6d7f746c9badc3da94681a1a660faeca39 + md5: 35c95aad3ab99e0a428c2e02e8b8e282 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 21037 + timestamp: 1752240015504 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-compression-0.3.1-ha8a2810_6.conda + sha256: 760d399e54d5f9e86fdc76633e15e00e1b60fc90b15a446b9dce6f79443dcfd7 + md5: f00789373bfeb808ca267a34982352de + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 22931 + timestamp: 1752240036957 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-event-stream-0.5.5-h149bd38_3.conda + sha256: 74b7e5d727505efdb1786d9f4e0250484d23934a1d87f234dacacac97e440136 + md5: f9bff8c2a205ee0f28b0c61dad849a98 + depends: + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + license: Apache-2.0 + license_family: APACHE + size: 57675 + timestamp: 1753199060663 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-event-stream-0.5.5-hf5ae603_3.conda + sha256: f533b662b242fb0b8f001380cdc4fa31f2501c95b31e36d585efdf117913e096 + md5: 87d020af52c47edbd9f5abd9530c3c3a + depends: + - __osx >=10.13 + - libcxx >=19 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 51888 + timestamp: 1753199060561 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-event-stream-0.5.5-hd1b68e1_3.conda + sha256: d1021dfd8a5726af35b73207d90320dd60e85c257af4b4534fecfb34d31751a4 + md5: dc140e52c81171b62d306476b6738220 + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 51020 + timestamp: 1753199075045 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-event-stream-0.5.5-hccb7587_3.conda + sha256: c03c5c77ab447765ab2cfec6d231bafde6a07fc8de19cbb632ca7f849ec8fe29 + md5: cf4d3c01bd6b17c38a4de30ff81d4716 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + license: Apache-2.0 + license_family: APACHE + size: 56295 + timestamp: 1753199087984 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-http-0.10.4-h37a7233_0.conda + sha256: 6794d020d75cafa15e7677508c4bea5e8bca6233a5c7eb6c34397367ee37024c + md5: d828cb0be64d51e27eebe354a2907a98 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-compression >=0.3.1,<0.3.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 224186 + timestamp: 1753205774708 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-http-0.10.4-hb3df2dd_0.conda + sha256: 59e0d21fee5dbe9fe318d0a697d35e251199755457028f3b8944fd49d5f0450f + md5: 18ce47e0fab1b9b7fb3fea47a34186ad + depends: + - __osx >=10.13 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-compression >=0.3.1,<0.3.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 191794 + timestamp: 1753205776009 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-http-0.10.4-h09a8a51_0.conda + sha256: 54233587cfd6559e98b2d82c90c3721c059d1dd22518993967fb794e1b8d2d14 + md5: 73e8d2fb68c060de71369ebd5a9b8621 + depends: + - __osx >=11.0 + - aws-c-compression >=0.3.1,<0.3.2.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 170412 + timestamp: 1753205794763 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-http-0.10.4-h04b3cea_0.conda + sha256: 31e65a30b1c99fff0525cc27b5854dc3e3d18a78c13245ea20114f1a503cbd13 + md5: ec4a2bd790833c3ca079d0e656e3c261 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-compression >=0.3.1,<0.3.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 206269 + timestamp: 1753205802777 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-io-0.21.2-h6252d9a_1.conda + sha256: 01ab3fd74ccd1cd3ebdde72898e0c3b9ab23151b9cd814ac627e3efe88191d8e + md5: cf5e9b21384fdb75b15faf397551c247 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - s2n >=1.5.23,<1.5.24.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 180168 + timestamp: 1753465862916 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-io-0.21.2-h46f635e_1.conda + sha256: 1b44d16454c90c0201e9297ba937fd70c2e86569b18967e932a8dfbbdaee7d37 + md5: eb8c7b3617c0571f3586d57df50b1185 + depends: + - __osx >=10.15 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 181750 + timestamp: 1753465852316 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-io-0.21.2-hc6344be_1.conda + sha256: e872cc4ad2ebb2aee84c1bb8f86e1fb2b5505d8932f560f8dcac6d6436ebca88 + md5: 5b427cbf0259d0a50268901824df6331 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 175631 + timestamp: 1753465863221 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-io-0.21.2-h20b9e97_1.conda + sha256: 47d3d3cfa9d0628e297a574fb8e124ba32bf2779e8a8b2de26c8c2b30dcad27a + md5: 9b9b649cde9d96dd54b3899a130da1e6 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 181441 + timestamp: 1753465872617 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-mqtt-0.13.3-h19deb91_3.conda + sha256: 4f1b36a50f9d74267cc73740af252f1d6f2da21a6dbef3c0086df1a78c81ed6f + md5: 1680d64986f8263978c3624f677656c8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 216117 + timestamp: 1753306261844 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-mqtt-0.13.3-h6fbeeec_3.conda + sha256: 4bffd41ba1c97f2788f63fb637cd07ea509f7f469f7ae61e997b37bbc8f2f1bb + md5: bbfe8f57e247fabd15227d2c0801cb14 + depends: + - __osx >=10.13 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 188193 + timestamp: 1753306273062 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-mqtt-0.13.3-h625c29d_3.conda + sha256: 129cfcd2132dcc019f85d6259671ed13c0d5d3dfd287ea684bf625503fb8c3b5 + md5: 8937dc148e22c1c15d2f181e6b6eee5e + depends: + - __osx >=11.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 150189 + timestamp: 1753306324109 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-mqtt-0.13.3-h6b158f5_3.conda + sha256: e860df2e337dc0f1deb39f90420233a14de2f38529b7c0add526227a2eef0620 + md5: 16ff5efd5b9219df333171ec891952c1 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 206091 + timestamp: 1753306348261 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-s3-0.8.6-h800fcd2_2.conda + sha256: 886345904f41cdcd8ca4a540161d471d18de60871ffcce42242a4812fc90dcea + md5: 50e0900a33add0c715f17648de6be786 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - openssl >=3.5.1,<4.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 137514 + timestamp: 1753335820784 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-s3-0.8.6-he7aa9d9_2.conda + sha256: 2b25912f0c528e98c6d033908068ca69918dbc0ea4d263b736151a9e3d90064d + md5: 72e2009c8ad840d2f22124aa3dacf931 + depends: + - __osx >=10.13 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 121694 + timestamp: 1753335830764 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-s3-0.8.6-h6ded10d_2.conda + sha256: cd3e9f1ef88e6f77909ddad68d99a620546a94d26ce36c6802a8c04905221cd0 + md5: 19821ae3d32c9d446a899562b35ef89e + depends: + - __osx >=11.0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 117740 + timestamp: 1753335826708 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-s3-0.8.6-h46905be_2.conda + sha256: d91eee836c22436bef1b08ae3137181a9fe92c51803e8710e5e0ac039126f69c + md5: d15a4df142dbd6e39825cdf32025f7e4 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + license: Apache-2.0 + license_family: APACHE + size: 128957 + timestamp: 1753335843139 +- conda: https://prefix.dev/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h92c474e_1.conda + sha256: a9e071a584be0257b2ec6ab6e1f203e9d6b16d2da2233639432727ffbf424f3d + md5: 4ab554b102065910f098f88b40163835 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 59146 + timestamp: 1752240966518 +- conda: https://prefix.dev/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h7a4e982_1.conda + sha256: 85d1b9eb67e02f6a622dcc0c854683da8ccd059d59b80a1ffa7f927eac771b93 + md5: 9ab61d370fc6e4caeb5525ef92e2d477 + depends: + - __osx >=10.13 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 55375 + timestamp: 1752240983413 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-habbe1e8_1.conda + sha256: cab7f54744619b88679c577c9ec8d56957bc8f6829e9966a7e50857fbc6c756d + md5: 9d77627725afb71b57f38355ee9e2829 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 53149 + timestamp: 1752240972623 +- conda: https://prefix.dev/conda-forge/win-64/aws-c-sdkutils-0.2.4-ha8a2810_1.conda + sha256: b8c7637ad8069ace0f79cc510275b01787c9d478888d4e548980ef2ca61f19c5 + md5: afbb1a7d671fc81c97daeac8ff6c54e0 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 56289 + timestamp: 1752240989872 +- conda: https://prefix.dev/conda-forge/linux-64/aws-checksums-0.2.7-h92c474e_2.conda + sha256: 7168007329dfb1c063cd5466b33a1f2b8a28a00f587a0974d97219432361b4db + md5: 248831703050fe9a5b2680a7589fdba9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 76748 + timestamp: 1752241068761 +- conda: https://prefix.dev/conda-forge/osx-64/aws-checksums-0.2.7-h7a4e982_2.conda + sha256: 523e5d6ffb58a333c6e4501e18120b53290ddad1f879e72ac7f58b15b505f92a + md5: a8a7aa3088b1310cebbc4777f887bd80 + depends: + - __osx >=10.13 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 75320 + timestamp: 1752241080472 +- conda: https://prefix.dev/conda-forge/osx-arm64/aws-checksums-0.2.7-habbe1e8_2.conda + sha256: 648c3d23df53b4cea1d551e4e54a544284be5436af5453296ed8184d970efc3a + md5: f3f6fef7c8d8ce7f80df37e4aaaf6b93 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 74030 + timestamp: 1752241089866 +- conda: https://prefix.dev/conda-forge/win-64/aws-checksums-0.2.7-ha8a2810_2.conda + sha256: 2c2f5b176fb8c0f15c6bc5edea0b2dd3d56b58e8b1124eb0f592665cec5dfc35 + md5: d6342b48cb2f43df847ee39e0858813a + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 92982 + timestamp: 1752241099189 +- conda: https://prefix.dev/conda-forge/linux-64/awscli-2.28.18-py313h78bf25f_0.conda + sha256: b9772bf3045137b0683fc78b94579da318e2573fc13024cb22d12c144f63af02 + md5: 1c9fcdb552ef81fe29f62041d87d0520 + depends: + - awscrt 0.27.5 + - colorama >=0.2.5,<0.4.7 + - distro >=1.5.0,<1.9.0 + - docutils >=0.10,<0.20 + - jmespath >=0.7.1,<1.1.0 + - prompt-toolkit >=3.0.24,<3.0.52 + - python >=3.13,<3.14.0a0 + - python-dateutil >=2.1,<=2.9.0 + - python_abi 3.13.* *_cp313 + - ruamel.yaml >=0.15.0,<=0.17.21 + - ruamel.yaml.clib >=0.2.0,<=0.2.12 + - urllib3 >=1.25.4,<1.27 + license: Apache-2.0 + size: 13773491 + timestamp: 1756250591722 +- conda: https://prefix.dev/conda-forge/osx-64/awscli-2.28.18-py312hb401068_0.conda + sha256: ebd3503a7267cad566ee0ad45551dcd299b2aeaf36dfae3f4fe6f7936741b542 + md5: 2fa46dcba465faad053eeec987214801 + depends: + - awscrt 0.27.5 + - colorama >=0.2.5,<0.4.7 + - distro >=1.5.0,<1.9.0 + - docutils >=0.10,<0.20 + - jmespath >=0.7.1,<1.1.0 + - prompt-toolkit >=3.0.24,<3.0.52 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.1,<=2.9.0 + - python_abi 3.12.* *_cp312 + - ruamel.yaml >=0.15.0,<=0.17.21 + - ruamel.yaml.clib >=0.2.0,<=0.2.12 + - urllib3 >=1.25.4,<1.27 + license: Apache-2.0 + size: 13839094 + timestamp: 1756250955502 +- conda: https://prefix.dev/conda-forge/osx-arm64/awscli-2.28.18-py313h8f79df9_0.conda + sha256: 32d69664ca896444cd397c0f430a1e4557ada85c97120cfd320d07694df27802 + md5: 2ab6fc4dee8740c23f25995105dd0c4e + depends: + - awscrt 0.27.5 + - colorama >=0.2.5,<0.4.7 + - distro >=1.5.0,<1.9.0 + - docutils >=0.10,<0.20 + - jmespath >=0.7.1,<1.1.0 + - prompt-toolkit >=3.0.24,<3.0.52 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python-dateutil >=2.1,<=2.9.0 + - python_abi 3.13.* *_cp313 + - ruamel.yaml >=0.15.0,<=0.17.21 + - ruamel.yaml.clib >=0.2.0,<=0.2.12 + - urllib3 >=1.25.4,<1.27 + license: Apache-2.0 + size: 13812432 + timestamp: 1756251077013 +- conda: https://prefix.dev/conda-forge/win-64/awscli-2.28.18-py312h2e8e312_0.conda + sha256: af521df0bba2c3a1954bc973e17eb375a9cd129fe03af1a931c90b60800f3703 + md5: a75c506cb270259d00c4b36a2d3caa88 + depends: + - awscrt 0.27.5 + - colorama >=0.2.5,<0.4.7 + - distro >=1.5.0,<1.9.0 + - docutils >=0.10,<0.20 + - jmespath >=0.7.1,<1.1.0 + - prompt-toolkit >=3.0.24,<3.0.52 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.1,<=2.9.0 + - python_abi 3.12.* *_cp312 + - ruamel.yaml >=0.15.0,<=0.17.21 + - ruamel.yaml.clib >=0.2.0,<=0.2.12 + - urllib3 >=1.25.4,<1.27 + license: Apache-2.0 + size: 13818030 + timestamp: 1756250796766 +- conda: https://prefix.dev/conda-forge/linux-64/awscrt-0.27.5-py313ha03020b_0.conda + sha256: 59608029a8e952a5e0147a29a1812e2aa009059fbf27a8ff3c1943820904f1b5 + md5: 5e0612b7958699f9e3851adbd252b801 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-c-event-stream >=0.5.5,<0.5.6.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - python_abi 3.13.* *_cp313 + - aws-c-s3 >=0.8.6,<0.8.7.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-mqtt >=0.13.3,<0.13.4.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - s2n >=1.5.23,<1.5.24.0a0 + license: Apache-2.0 + license_family: APACHE + size: 257621 + timestamp: 1753940907520 +- conda: https://prefix.dev/conda-forge/osx-64/awscrt-0.27.5-py312hef7181d_0.conda + sha256: f982737141a124bfd5680eae76d561ac8ddfe854a8c48ed0ebe2b991cef8d9ca + md5: 953b5d278f81b2aa58f5193dcbb1dca4 + depends: + - python + - __osx >=10.13 + - aws-c-mqtt >=0.13.3,<0.13.4.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-c-event-stream >=0.5.5,<0.5.6.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-s3 >=0.8.6,<0.8.7.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - python_abi 3.12.* *_cp312 + - aws-c-io >=0.21.2,<0.21.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 238594 + timestamp: 1753940910016 +- conda: https://prefix.dev/conda-forge/osx-arm64/awscrt-0.27.5-py313h3c677f0_0.conda + sha256: 7810574c90089fa3af5af844d70bd956944e22be1427347125d340acc4adb9a4 + md5: 4245bcbef8c84660748d5f0925364449 + depends: + - python + - __osx >=11.0 + - python 3.13.* *_cp313 + - aws-c-http >=0.10.4,<0.10.5.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-event-stream >=0.5.5,<0.5.6.0a0 + - python_abi 3.13.* *_cp313 + - aws-c-mqtt >=0.13.3,<0.13.4.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-c-s3 >=0.8.6,<0.8.7.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 247508 + timestamp: 1753940913665 +- conda: https://prefix.dev/conda-forge/win-64/awscrt-0.27.5-py312h44c0e62_0.conda + sha256: c01c732b448fad070a16ad61ea7c0536f7460a03053e31f50e8fabf1a0cddc5d + md5: b20fd1f5949b6203c35347297ca31437 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-event-stream >=0.5.5,<0.5.6.0a0 + - aws-c-cal >=0.9.2,<0.9.3.0a0 + - aws-c-auth >=0.9.0,<0.9.1.0a0 + - aws-c-mqtt >=0.13.3,<0.13.4.0a0 + - aws-c-common >=0.12.4,<0.12.5.0a0 + - aws-c-s3 >=0.8.6,<0.8.7.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - python_abi 3.12.* *_cp312 + - aws-c-io >=0.21.2,<0.21.3.0a0 + - aws-c-http >=0.10.4,<0.10.5.0a0 + license: Apache-2.0 + license_family: APACHE + size: 3315593 + timestamp: 1753940927518 - conda: https://prefix.dev/conda-forge/linux-64/binutils-2.43-h4852527_4.conda sha256: 99a94eead18e7704225ac43682cce3f316fd33bc483749c093eaadef1d31de75 md5: 29782348a527eda3ecfc673109d28e93 @@ -447,6 +1447,65 @@ packages: license_family: GPL size: 35657 timestamp: 1740155500723 +- conda: https://prefix.dev/conda-forge/linux-64/brotli-python-1.1.0-py313h46c70d0_3.conda + sha256: e510ad1db7ea882505712e815ff02514490560fd74b5ec3a45a6c7cf438f754d + md5: 2babfedd9588ad40c7113ddfe6a5ca82 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_3 + license: MIT + license_family: MIT + size: 350295 + timestamp: 1749230225293 +- conda: https://prefix.dev/conda-forge/osx-64/brotli-python-1.1.0-py312haafddd8_3.conda + sha256: d1a8635422d99b4b7cc1b35d62d1a5c392ae0a4d74e0a44bf190916a21180ba3 + md5: 11489c0fc22f550acf63da5e7ec7304d + depends: + - __osx >=10.13 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 h6e16a3a_3 + license: MIT + license_family: MIT + size: 367262 + timestamp: 1749230495846 +- conda: https://prefix.dev/conda-forge/osx-arm64/brotli-python-1.1.0-py313h928ef07_3.conda + sha256: 0f2f3c7b3f6a19a27b2878b58bfd16af69cea90d0d3052a2a0b4e0a2cbede8f9 + md5: 3030bcec50cc407b596f9311eeaa611f + depends: + - __osx >=11.0 + - libcxx >=18 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + constrains: + - libbrotlicommon 1.1.0 h5505292_3 + license: MIT + license_family: MIT + size: 338938 + timestamp: 1749230456550 +- conda: https://prefix.dev/conda-forge/win-64/brotli-python-1.1.0-py312h275cf98_3.conda + sha256: d5c18a90220853c86f7cc23db62b32b22c6c5fe5d632bc111fc1e467c9fd776f + md5: a87a39f9eb9fd5f171b13d8c79f7a99a + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + constrains: + - libbrotlicommon 1.1.0 h2466b09_3 + license: MIT + license_family: MIT + size: 321941 + timestamp: 1749231054102 - conda: https://prefix.dev/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d md5: 62ee74e96c5ebb0af99386de58cf9553 @@ -555,6 +1614,22 @@ packages: license: ISC size: 158144 timestamp: 1738298224464 +- conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-h4c7d964_0.conda + sha256: 3b82f62baad3fd33827b01b0426e8203a2786c8f452f633740868296bcbe8485 + md5: c9e0c0f82f6e63323827db462b40ede8 + depends: + - __win + license: ISC + size: 154489 + timestamp: 1754210967212 +- conda: https://prefix.dev/conda-forge/noarch/ca-certificates-2025.8.3-hbd8a1cb_0.conda + sha256: 837b795a2bb39b75694ba910c13c15fa4998d4bb2a622c214a6a5174b2ae53d1 + md5: 74784ee3d225fc3dca89edb635b4e5cc + depends: + - __unix + license: ISC + size: 154402 + timestamp: 1754210968730 - conda: https://prefix.dev/conda-forge/osx-64/ca-certificates-2025.1.31-h8857fd0_0.conda sha256: 42e911ee2d8808eacedbec46d99b03200a6138b8e8a120bd8acabe1cac41c63b md5: 3418b6c8cac3e71c0bc089fc5ea53042 @@ -992,6 +2067,15 @@ packages: license_family: BSD size: 15107993 timestamp: 1684462053404 +- conda: https://prefix.dev/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 - conda: https://prefix.dev/conda-forge/osx-64/compiler-rt-19.1.7-h52031e2_0.conda sha256: 781b70f7475d387183fba59b5d88e874ec976458b893ec4e8c4c2564944aa680 md5: 8098d99b4c30adb2f9cc18f8584d0b45 @@ -1107,6 +2191,52 @@ packages: license_family: APACHE size: 274151 timestamp: 1733238487461 +- conda: https://prefix.dev/conda-forge/noarch/distro-1.8.0-pyhd8ed1ab_0.conda + sha256: 0d01c4da6d4f0a935599210f82ac0630fa9aeb4fc37cbbc78043a932a39ec4f3 + md5: 67999c5465064480fa8016d00ac768f6 + depends: + - python >=3.6 + license: Apache-2.0 + license_family: APACHE + size: 40854 + timestamp: 1675116355989 +- conda: https://prefix.dev/conda-forge/linux-64/docutils-0.18.1-py313h78bf25f_1.conda + sha256: 101d73deed8d839335d75ff2cdcb548ba666b699e16fd11488644efb0644cf7f + md5: 0d72df6ebc7870ee7f7486dde11b7732 + depends: + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + size: 901056 + timestamp: 1755942635216 +- conda: https://prefix.dev/conda-forge/osx-64/docutils-0.18.1-py312hb401068_1.conda + sha256: 2af045a5ba0b61e77d849c5dca966a7f453eaa26c58d14e5127b95e535adeb07 + md5: 0d62b036556079c60714f0c6ea988302 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + size: 896091 + timestamp: 1755942887880 +- conda: https://prefix.dev/conda-forge/osx-arm64/docutils-0.18.1-py313h8f79df9_1.conda + sha256: 18c04895a1f7663cb7f39e6f4cd696d8932ead398ef0c514ccfeaf7372269912 + md5: 3872bbedbb08bc79ad5b78792debb698 + depends: + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + size: 902837 + timestamp: 1755942805232 +- conda: https://prefix.dev/conda-forge/win-64/docutils-0.18.1-py312h2e8e312_1.conda + sha256: 517fe814fbfe570978369bc6dd9f951739293cf90905213204f30b2c29df7946 + md5: 766c498c3e50dac8e4605d6ac9dcf5a8 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + size: 897162 + timestamp: 1755942744913 - conda: https://prefix.dev/conda-forge/linux-64/expat-2.6.4-h5888daf_0.conda sha256: 1848c7db9e264e3b8036ee133d570dd880422983cd20dd9585a505289606d276 md5: 1d6afef758879ef5ee78127eb4cd2c4a @@ -1303,6 +2433,15 @@ packages: license_family: MIT size: 78619 timestamp: 1740257841338 +- conda: https://prefix.dev/conda-forge/noarch/jmespath-1.0.1-pyhd8ed1ab_1.conda + sha256: 3d2f20ee7fd731e3ff55c189db9c43231bc8bde957875817a609c227bcb295c6 + md5: 972bdca8f30147135f951847b30399ea + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 23708 + timestamp: 1733229244590 - conda: https://prefix.dev/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda sha256: a922841ad80bd7b222502e65c07ecb67e4176c4fa5b03678a005f39fcc98be4b md5: ad8527bf134a90e1c9ed35fa0b64318c @@ -1431,6 +2570,17 @@ packages: license_family: GPL size: 671240 timestamp: 1740155456116 +- conda: https://prefix.dev/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda + sha256: 1a620f27d79217c1295049ba214c2f80372062fd251b569e9873d4a953d27554 + md5: 0be7c6e070c19105f966d3758448d018 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.44 + license: GPL-3.0-only + license_family: GPL + size: 676044 + timestamp: 1752032747103 - conda: https://prefix.dev/conda-forge/linux-64/libclang-cpp18.1-18.1.8-default_hb5137d0_7.conda sha256: 28982cf5038c5f141f4f6374ca3cb22d85e50bcee8ea76b3df8fe0de125672f2 md5: 8dacc063fe4ec190fd265d9b2638a90f @@ -1517,6 +2667,15 @@ packages: license_family: Apache size: 527924 timestamp: 1736877256721 +- conda: https://prefix.dev/conda-forge/osx-64/libcxx-20.1.8-h3d58e20_1.conda + sha256: 9643d6c5a94499cddb5ae1bccc4f78aef8cfd77bcf6b37ad325bc7232a8a870f + md5: d2db320b940047515f7a27f870984fe7 + depends: + - __osx >=10.13 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 564830 + timestamp: 1752814841086 - conda: https://prefix.dev/conda-forge/osx-arm64/libcxx-19.1.7-ha82da77_0.conda sha256: 776092346da87a2a23502e14d91eb0c32699c4a1522b7331537bd1c3751dcff5 md5: 5b3e1610ff8bd5443476b91d618f5b77 @@ -1526,6 +2685,15 @@ packages: license_family: Apache size: 523505 timestamp: 1736877862502 +- conda: https://prefix.dev/conda-forge/osx-arm64/libcxx-20.1.8-hf598326_1.conda + sha256: 119b3ac75cb1ea29981e5053c2cb10d5f0b06fcc81b486cb7281f160daf673a1 + md5: a69ef3239d3268ef8602c7a7823fd982 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 568267 + timestamp: 1752814881595 - conda: https://prefix.dev/conda-forge/osx-64/libcxx-devel-19.1.7-h7c275be_1.conda sha256: d1ee08b0614d8f9bca84aa91b4015c5efa96162fd865590a126544243699dfc6 md5: 0f3f15e69e98ce9b3307c1d8309d1659 @@ -1613,6 +2781,18 @@ packages: license_family: MIT size: 73304 timestamp: 1730967041968 +- conda: https://prefix.dev/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda + sha256: da2080da8f0288b95dd86765c801c6e166c4619b910b11f9a8446fb852438dc2 + md5: 4211416ecba1866fab0c6470986c22d6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.7.1.* + license: MIT + license_family: MIT + size: 74811 + timestamp: 1752719572741 - conda: https://prefix.dev/conda-forge/osx-64/libexpat-2.6.4-h240833e_0.conda sha256: d10f43d0c5df6c8cf55259bce0fe14d2377eed625956cddce06f58827d288c59 md5: 20307f4049a735a78a29073be1be2626 @@ -1624,6 +2804,17 @@ packages: license_family: MIT size: 70758 timestamp: 1730967204736 +- conda: https://prefix.dev/conda-forge/osx-64/libexpat-2.7.1-h21dd04a_0.conda + sha256: 689862313571b62ee77ee01729dc093f2bf25a2f99415fcfe51d3a6cd31cce7b + md5: 9fdeae0b7edda62e989557d645769515 + depends: + - __osx >=10.13 + constrains: + - expat 2.7.1.* + license: MIT + license_family: MIT + size: 72450 + timestamp: 1752719744781 - conda: https://prefix.dev/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 md5: 38d2656dd914feb0cab8c629370768bf @@ -1635,6 +2826,17 @@ packages: license_family: MIT size: 64693 timestamp: 1730967175868 +- conda: https://prefix.dev/conda-forge/osx-arm64/libexpat-2.7.1-hec049ff_0.conda + sha256: 8fbb17a56f51e7113ed511c5787e0dec0d4b10ef9df921c4fd1cccca0458f648 + md5: b1ca5f21335782f71a8bd69bdc093f67 + depends: + - __osx >=11.0 + constrains: + - expat 2.7.1.* + license: MIT + license_family: MIT + size: 65971 + timestamp: 1752719657566 - conda: https://prefix.dev/conda-forge/win-64/libexpat-2.6.4-he0c23c2_0.conda sha256: 0c0447bf20d1013d5603499de93a16b6faa92d7ead870d96305c0f065b6a5a12 md5: eb383771c680aa792feb529eaf9df82f @@ -1648,6 +2850,19 @@ packages: license_family: MIT size: 139068 timestamp: 1730967442102 +- conda: https://prefix.dev/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda + sha256: 8432ca842bdf8073ccecf016ccc9140c41c7114dc4ec77ca754551c01f780845 + md5: 3608ffde260281fa641e70d6e34b1b96 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - expat 2.7.1.* + license: MIT + license_family: MIT + size: 141322 + timestamp: 1752719767870 - conda: https://prefix.dev/conda-forge/linux-64/libffi-3.4.6-h2dba641_0.conda sha256: 67a6c95e33ebc763c1adc3455b9a9ecde901850eb2fceb8e646cc05ef3a663da md5: e3eb7806380bc8bcecba6d749ad5f026 @@ -1658,6 +2873,16 @@ packages: license_family: MIT size: 53415 timestamp: 1739260413716 +- conda: https://prefix.dev/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + sha256: 764432d32db45466e87f10621db5b74363a9f847d2b8b1f9743746cd160f06ab + md5: ede4673863426c0883c0063d853bbd85 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 57433 + timestamp: 1743434498161 - conda: https://prefix.dev/conda-forge/osx-64/libffi-3.4.6-h281671d_0.conda sha256: 7805fdc536a3da7fb63dc48e040105cd4260c69a1d2bf5804dadd31bde8bab51 md5: b8667b0d0400b8dcb6844d8e06b2027d @@ -1667,6 +2892,15 @@ packages: license_family: MIT size: 47258 timestamp: 1739260651925 +- conda: https://prefix.dev/conda-forge/osx-64/libffi-3.4.6-h281671d_1.conda + sha256: 6394b1bc67c64a21a5cc73d1736d1d4193a64515152e861785c44d2cfc49edf3 + md5: 4ca9ea59839a9ca8df84170fab4ceb41 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + size: 51216 + timestamp: 1743434595269 - conda: https://prefix.dev/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca md5: 086914b672be056eb70fd4285b6783b6 @@ -1674,6 +2908,15 @@ packages: license_family: MIT size: 39020 timestamp: 1636488587153 +- conda: https://prefix.dev/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + sha256: c6a530924a9b14e193ea9adfe92843de2a806d1b7dbfd341546ece9653129e60 + md5: c215a60c2935b517dcda8cad4705734d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 39839 + timestamp: 1743434670405 - conda: https://prefix.dev/conda-forge/win-64/libffi-3.4.6-h537db12_0.conda sha256: 77922d8dd2faf88ac6accaeebf06409d1820486fde710cff6b554d12273e46be md5: 31d5107f75b2f204937728417e2e39e5 @@ -1685,6 +2928,17 @@ packages: license_family: MIT size: 40830 timestamp: 1739260917585 +- conda: https://prefix.dev/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda + sha256: d3b0b8812eab553d3464bbd68204f007f1ebadf96ce30eb0cbc5159f72e353f5 + md5: 85d8fa5e55ed8f93f874b3b23ed54ec6 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: MIT + license_family: MIT + size: 44978 + timestamp: 1743435053850 - conda: https://prefix.dev/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda sha256: 3a572d031cb86deb541d15c1875aaa097baefc0c580b54dc61f5edab99215792 md5: ef504d1acbd74b7cc6849ef8af47dd03 @@ -1711,6 +2965,19 @@ packages: license_family: GPL size: 824921 timestamp: 1750808216066 +- conda: https://prefix.dev/conda-forge/linux-64/libgcc-15.1.0-h767d61c_4.conda + sha256: 144e35c1c2840f2dc202f6915fc41879c19eddbb8fa524e3ca4aa0d14018b26f + md5: f406dcbb2e7bef90d793e50e79a2882b + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==15.1.0=*_4 + - libgomp 15.1.0 h767d61c_4 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 824153 + timestamp: 1753903866511 - conda: https://prefix.dev/conda-forge/win-64/libgcc-14.2.0-h1383e82_2.conda sha256: fddf2fc037bc95adb3b369e8866da8a71b6a67ebcfc4d7035ac4208309dc9e72 md5: 4a74c1461a0ba47a3346c04bdccbe2ad @@ -1752,6 +3019,15 @@ packages: license_family: GPL size: 29033 timestamp: 1750808224854 +- conda: https://prefix.dev/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_4.conda + sha256: 76ceac93ed98f208363d6e9c75011b0ff7b97b20f003f06461a619557e726637 + md5: 28771437ffcd9f3417c66012dc49a3be + depends: + - libgcc 15.1.0 h767d61c_4 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 29249 + timestamp: 1753903872571 - conda: https://prefix.dev/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda sha256: eea6c3cf22ad739c279b4d665e6cf20f8081f483b26a96ddd67d4df3c88dfa0a md5: 530566b68c3b8ce7eec4cd047eae19fe @@ -1814,6 +3090,15 @@ packages: license_family: GPL size: 447068 timestamp: 1750808138400 +- conda: https://prefix.dev/conda-forge/linux-64/libgomp-15.1.0-h767d61c_4.conda + sha256: e0487a8fec78802ac04da0ac1139c3510992bc58a58cde66619dde3b363c2933 + md5: 3baf8976c96134738bba224e9ef6b1e5 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 447289 + timestamp: 1753903801049 - conda: https://prefix.dev/conda-forge/win-64/libgomp-14.2.0-h1383e82_2.conda sha256: 674ec5f1bf319eac98d0d6ecb9c38e0192f3cf41969a5621d62a7e695e1aa9f3 md5: dd6b1ab49e28bcb6154cd131acec985b @@ -1938,6 +3223,17 @@ packages: license: 0BSD size: 111357 timestamp: 1738525339684 +- conda: https://prefix.dev/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 + md5: 1a580f7796c7bf6393fddb8bbbde58dc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 112894 + timestamp: 1749230047870 - conda: https://prefix.dev/conda-forge/osx-64/liblzma-5.6.4-hd471939_0.conda sha256: a895b5b16468a6ed436f022d72ee52a657f9b58214b91fabfab6230e3592a6dd md5: db9d7b0152613f097cdb61ccf9f70ef5 @@ -1946,6 +3242,16 @@ packages: license: 0BSD size: 103749 timestamp: 1738525448522 +- conda: https://prefix.dev/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda + sha256: 7e22fd1bdb8bf4c2be93de2d4e718db5c548aa082af47a7430eb23192de6bb36 + md5: 8468beea04b9065b9807fc8b9cdc5894 + depends: + - __osx >=10.13 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 104826 + timestamp: 1749230155443 - conda: https://prefix.dev/conda-forge/osx-arm64/liblzma-5.6.4-h39f12f2_0.conda sha256: 560c59d3834cc652a84fb45531bd335ad06e271b34ebc216e380a89798fe8e2c md5: e3fd1f8320a100f2b210e690a57cd615 @@ -1954,6 +3260,16 @@ packages: license: 0BSD size: 98945 timestamp: 1738525462560 +- conda: https://prefix.dev/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 + md5: d6df911d4564d77c4374b02552cb17d1 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 92286 + timestamp: 1749230283517 - conda: https://prefix.dev/conda-forge/win-64/liblzma-5.6.4-h2466b09_0.conda sha256: 3f552b0bdefdd1459ffc827ea3bf70a6a6920c7879d22b6bfd0d73015b55227b md5: c48f6ad0ef0a555b27b233dfcab46a90 @@ -1964,6 +3280,18 @@ packages: license: 0BSD size: 104465 timestamp: 1738525557254 +- conda: https://prefix.dev/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda + sha256: 55764956eb9179b98de7cc0e55696f2eff8f7b83fc3ebff5e696ca358bca28cc + md5: c15148b2e18da456f5108ccb5e411446 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 104935 + timestamp: 1749230611612 - conda: https://prefix.dev/conda-forge/linux-64/liblzma-devel-5.6.4-hb9d3cd8_0.conda sha256: 34928b36a3946902196a6786db80c8a4a97f6c9418838d67be90a1388479a682 md5: 5ab1a0df19c8f3ec00d5e63458e0a420 @@ -1992,6 +3320,25 @@ packages: license: 0BSD size: 113696 timestamp: 1738525475905 +- conda: https://prefix.dev/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda + sha256: 3aa92d4074d4063f2a162cd8ecb45dccac93e543e565c01a787e16a43501f7ee + md5: c7e925f37e3b40d893459e625f6a53f1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: BSD-2-Clause + license_family: BSD + size: 91183 + timestamp: 1748393666725 +- conda: https://prefix.dev/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + sha256: 0a1875fc1642324ebd6c4ac864604f3f18f57fbcf558a8264f6ced028a3c75b2 + md5: 85ccccb47823dd9f7a99d2c7f530342f + depends: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + size: 71829 + timestamp: 1748393749336 - conda: https://prefix.dev/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda sha256: 1910c5306c6aa5bcbd623c3c930c440e9c77a5a019008e1487810e3c1d3716cb md5: 700ac6ea6d53d5510591c4344d5c989a @@ -2067,6 +3414,16 @@ packages: license: Unlicense size: 915956 timestamp: 1739953155793 +- conda: https://prefix.dev/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda + sha256: 6d9c32fc369af5a84875725f7ddfbfc2ace795c28f246dc70055a79f9b2003da + md5: 0b367fad34931cb79e0d6b7e5c06bb1c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: blessing + size: 932581 + timestamp: 1753948484112 - conda: https://prefix.dev/conda-forge/osx-64/libsqlite-3.49.1-hdb6dae5_1.conda sha256: 859e5f1a39e320b3575b98b7a80ab7c62b337465b12b181c8bbe305fecc9430b md5: 7958168c20fbbc5014e1fbda868ed700 @@ -2076,6 +3433,15 @@ packages: license: Unlicense size: 977598 timestamp: 1739953439197 +- conda: https://prefix.dev/conda-forge/osx-64/libsqlite-3.50.4-h39a8b3b_0.conda + sha256: 466366b094c3eb4b1d77320530cbf5400e7a10ab33e4824c200147488eebf7a6 + md5: 156bfb239b6a67ab4a01110e6718cbc4 + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + license: blessing + size: 980121 + timestamp: 1753948554003 - conda: https://prefix.dev/conda-forge/osx-arm64/libsqlite-3.49.1-h3f77e49_1.conda sha256: 266639fb10ca92287961574b0b4d6031fa40dd9d723d64a0fcb08513a24dab03 md5: c83357a21092bd952933c36c5cb4f4d6 @@ -2085,6 +3451,16 @@ packages: license: Unlicense size: 898767 timestamp: 1739953312379 +- conda: https://prefix.dev/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_0.conda + sha256: 802ebe62e6bc59fc26b26276b793e0542cfff2d03c086440aeaf72fb8bbcec44 + md5: 1dcb0468f5146e38fae99aef9656034b + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libzlib >=1.3.1,<2.0a0 + license: blessing + size: 902645 + timestamp: 1753948599139 - conda: https://prefix.dev/conda-forge/win-64/libsqlite-3.49.1-h67fdade_1.conda sha256: 08669790e4de89201079e93e8a8d8c51a3cd57a19dd559bb0d5bc6c9a7970b99 md5: 88931435901c1f13d4e3a472c24965aa @@ -2095,6 +3471,16 @@ packages: license: Unlicense size: 1081190 timestamp: 1739953491995 +- conda: https://prefix.dev/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda + sha256: 5dc4f07b2d6270ac0c874caec53c6984caaaa84bc0d3eb593b0edf3dc8492efa + md5: ccb20d946040f86f0c05b644d5eadeca + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: blessing + size: 1288499 + timestamp: 1753948889360 - conda: https://prefix.dev/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda sha256: 50e47fd9c4f7bf841a11647ae7486f65220cfc988ec422a4475fe8d5a823824d md5: 1f5a58e686b13bcfde88b93f547d23fe @@ -2146,6 +3532,16 @@ packages: license_family: GPL size: 3896407 timestamp: 1750808251302 +- conda: https://prefix.dev/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_4.conda + sha256: b5b239e5fca53ff90669af1686c86282c970dd8204ebf477cf679872eb6d48ac + md5: 3c376af8888c386b9d3d1c2701e2f3ab + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.1.0 h767d61c_4 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3903453 + timestamp: 1753903894186 - conda: https://prefix.dev/conda-forge/noarch/libstdcxx-devel_linux-64-14.3.0-h85bb3a7_103.conda sha256: 0a2a6298beb7225bb850011241b5be9dbeef52e5942d74c592f577b16c38334f md5: 8f310e4b92c1b1ec1bd3ee16931c149f @@ -2437,6 +3833,66 @@ packages: license_family: MIT size: 83849 timestamp: 1737310619948 +- conda: https://prefix.dev/conda-forge/linux-64/minio-client-2025.07.21.05.28.08-hfc2019e_0.conda + sha256: fd4b0e7f78eeb9c8ac3e81d4ac47b762a5bcf89a81cb9f4de7d9f87128610da3 + md5: 123268b66158fe6b737c08badf07522d + license: AGPL-3.0-or-later + license_family: AGPL + size: 20940556 + timestamp: 1753336475749 +- conda: https://prefix.dev/conda-forge/osx-64/minio-client-2025.07.21.05.28.08-hccc6df8_0.conda + sha256: 41ffb10fb94cb41e1314dbc6f3edcdba5c68901b427e0a97f3191b5bee6459a2 + md5: cd3c38fc2e8d4c410d9d368376ce4c6f + constrains: + - __osx >=10.12 + license: AGPL-3.0-or-later + license_family: AGPL + size: 20759192 + timestamp: 1753336532300 +- conda: https://prefix.dev/conda-forge/osx-arm64/minio-client-2025.07.21.05.28.08-h820172f_0.conda + sha256: f9e2aa71de6c58dcc320fb29abb7d6330aa5397a2d987d1f84e7016ecffa7de3 + md5: 284b0a86cfdb402a528e576f7091549e + license: AGPL-3.0-or-later + license_family: AGPL + size: 19334349 + timestamp: 1753336520521 +- conda: https://prefix.dev/conda-forge/win-64/minio-client-2025.07.21.05.28.08-h11686cb_0.conda + sha256: 029eafeebb1efb2b545f122a807e44590454584530530f048cecd54b37ba4c28 + md5: 31e6a6420e24de417433891e2d67efc8 + license: AGPL-3.0-or-later + license_family: AGPL + size: 20826403 + timestamp: 1753336487247 +- conda: https://prefix.dev/conda-forge/linux-64/minio-server-2025.01.20.14.49.07-hbcca054_1.conda + sha256: 64ec235861580506d91aea596e7459afe2587322567246a35b247318606db43b + md5: 0b791bd9c5c9425236b84bc6d5c8680a + license: AGPL-3.0-only + license_family: AGPL + size: 32642210 + timestamp: 1740608434726 +- conda: https://prefix.dev/conda-forge/osx-64/minio-server-2025.01.20.14.49.07-h8857fd0_1.conda + sha256: 0dc2b0b3fd446cbb3d9ee724d2411933d3cbb90d10165a47e4b238d9d93f4643 + md5: f3de9c14bf2ebc2d46329296dc4c5e17 + constrains: + - __osx>=10.12 + license: AGPL-3.0-only + license_family: AGPL + size: 33078891 + timestamp: 1740608504514 +- conda: https://prefix.dev/conda-forge/osx-arm64/minio-server-2025.01.20.14.49.07-hf0a4a13_1.conda + sha256: b845aca8956c646c4e13b257d33d18922f48dae813950735537d7a0772edf71a + md5: 5a8bb8e76f4920d8eedc7bd6444a9c85 + license: AGPL-3.0-only + license_family: AGPL + size: 31639530 + timestamp: 1740608475917 +- conda: https://prefix.dev/conda-forge/win-64/minio-server-2025.01.20.14.49.07-h56e8100_1.conda + sha256: a173a0d3f6dad03b6fcc12bebb92a488da73711da166ed4e4d326fef4cf5beba + md5: 85e2181ceea1e4a95100609be9ff9881 + license: AGPL-3.0-only + license_family: AGPL + size: 33154212 + timestamp: 1740609084606 - conda: https://prefix.dev/conda-forge/linux-64/mold-2.36.0-hff13881_1.conda sha256: 6eacb23d89c13dec2184560728d1351c82c52c1aaba85377f056b8e2d15b6b7d md5: 0274ce570b92fcc1dd23915c526457b4 @@ -2488,6 +3944,62 @@ packages: license_family: BSD size: 34574 timestamp: 1734112236147 +- conda: https://prefix.dev/conda-forge/linux-64/nushell-0.106.1-hb0b18c3_0.conda + sha256: 6d4155e07b0e2e80dba11a8ea1e6dddba8640b487b287bd602924928e95d6e34 + md5: 97acc5a3ddc58e0b5ebd10218c32cbcb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 9578913 + timestamp: 1753855313062 +- conda: https://prefix.dev/conda-forge/osx-64/nushell-0.106.1-h088d292_0.conda + sha256: 884ec715aeb831837371fadbe4d685bdc139df76045b9270608a021f39265b53 + md5: 16686ceed82b3ee44077fc59f8a1c2c3 + depends: + - __osx >=10.13 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + constrains: + - __osx >=10.13 + license: MIT + license_family: MIT + size: 9576373 + timestamp: 1753855501921 +- conda: https://prefix.dev/conda-forge/osx-arm64/nushell-0.106.1-h1b39ca6_0.conda + sha256: 032aa5aa79027538eec197a31cb8095278582b4fb8bf18c6727c37c2dc457db7 + md5: 65dc32f130ca4e43df6aa34672d76f52 + depends: + - __osx >=11.0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 9265972 + timestamp: 1753855652097 +- conda: https://prefix.dev/conda-forge/win-64/nushell-0.106.1-hdab3696_0.conda + sha256: f065a56141d5a07659eb975af9ee939301a77d8021cb88795444c2361d7205b2 + md5: 25a6e8cb1ae00521b1d741a7f06cc012 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 8813615 + timestamp: 1753857173692 - conda: https://prefix.dev/conda-forge/linux-64/openssl-3.4.1-h7b32b05_0.conda sha256: cbf62df3c79a5c2d113247ddea5658e9ff3697b6e741c210656e239ecaf1768f md5: 41adf927e746dc75ecf0ef841c454e48 @@ -2499,6 +4011,17 @@ packages: license_family: Apache size: 2939306 timestamp: 1739301879343 +- conda: https://prefix.dev/conda-forge/linux-64/openssl-3.5.2-h26f9b46_0.conda + sha256: c9f54d4e8212f313be7b02eb962d0cb13a8dae015683a403d3accd4add3e520e + md5: ffffb341206dd0dab0c36053c048d621 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + size: 3128847 + timestamp: 1754465526100 - conda: https://prefix.dev/conda-forge/osx-64/openssl-3.3.0-h87427d6_3.conda sha256: 58ffbdce44ac18c6632a2ce1531d06e3fb2e855d40728ba3a2b709158b9a1c33 md5: ec504fefb403644d893adffb6e7a2dbe @@ -2521,6 +4044,16 @@ packages: license_family: Apache size: 2591479 timestamp: 1739302628009 +- conda: https://prefix.dev/conda-forge/osx-64/openssl-3.5.2-h6e31bce_0.conda + sha256: 8be57a11019666aa481122c54e29afd604405b481330f37f918e9fbcd145ef89 + md5: 22f5d63e672b7ba467969e9f8b740ecd + depends: + - __osx >=10.13 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2743708 + timestamp: 1754466962243 - conda: https://prefix.dev/conda-forge/osx-arm64/openssl-3.3.0-hfb2fe0b_3.conda sha256: 6f41c163ab57e7499dff092be4498614651f0f6432e12c2b9f06859a8bc39b75 md5: 730f618b008b3c13c1e3f973408ddd67 @@ -2543,6 +4076,16 @@ packages: license_family: Apache size: 2934522 timestamp: 1739301896733 +- conda: https://prefix.dev/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda + sha256: f6d1c87dbcf7b39fad24347570166dade1c533ae2d53c60a70fa4dc874ef0056 + md5: bcb0d87dfbc199d0a461d2c7ca30b3d8 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 3074848 + timestamp: 1754465710470 - conda: https://prefix.dev/conda-forge/win-64/openssl-3.3.0-h2466b09_3.conda sha256: 11b2513fceb20102bdc7f7656a59005acb9ecd0886b7cbfb9c13c2c953f2429b md5: d7fec5d3bb8fc0c8e266bf1ad350cec5 @@ -2569,6 +4112,18 @@ packages: license_family: Apache size: 8515197 timestamp: 1739304103653 +- conda: https://prefix.dev/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda + sha256: 2413f3b4606018aea23acfa2af3c4c46af786739ab4020422e9f0c2aec75321b + md5: 150d3920b420a27c0848acca158f94dc + depends: + - ca-certificates + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + size: 9275175 + timestamp: 1754467904482 - conda: https://prefix.dev/conda-forge/osx-arm64/pcre2-10.44-h297a79d_2.conda sha256: 83153c7d8fd99cab33c92ce820aa7bfed0f1c94fc57010cf227b6e3c50cb7796 md5: 147c83e5e44780c7492998acbacddf52 @@ -2670,6 +4225,18 @@ packages: license_family: MIT size: 34986 timestamp: 1734603755600 +- conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + sha256: ebc1bb62ac612af6d40667da266ff723662394c0ca78935340a5b5c14831227b + md5: d17ae9db4dc594267181bd199bf9a551 + depends: + - python >=3.9 + - wcwidth + constrains: + - prompt_toolkit 3.0.51 + license: BSD-3-Clause + license_family: BSD + size: 271841 + timestamp: 1744724188108 - conda: https://prefix.dev/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 md5: 12c566707c80111f9799308d9e265aef @@ -2680,6 +4247,27 @@ packages: license_family: BSD size: 110100 timestamp: 1733195786147 +- conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda + sha256: d016e04b0e12063fbee4a2d5fbb9b39a8d191b5a0042f0b8459188aedeabb0ca + md5: e2fd202833c4a981ce8a65974fe4abd1 + depends: + - __win + - python >=3.9 + - win_inet_pton + license: BSD-3-Clause + license_family: BSD + size: 21784 + timestamp: 1733217448189 +- conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 - conda: https://prefix.dev/conda-forge/linux-64/python-3.12.9-h9e4cc4f_0_cpython.conda sha256: 64fed5178f1e9c8ac0f572ac0ce37955f5dee7b2bcac665202bc14f1f7dd618a md5: 5665f0079432f8848079c811cdb537d5 @@ -2706,6 +4294,53 @@ packages: license: Python-2.0 size: 31581682 timestamp: 1739521496324 +- conda: https://prefix.dev/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda + build_number: 102 + sha256: c2cdcc98ea3cbf78240624e4077e164dc9d5588eefb044b4097c3df54d24d504 + md5: 89e07d92cf50743886f41638d58c4328 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - libgcc >=13 + - liblzma >=5.8.1,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.50.1,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.0,<4.0a0 + - python_abi 3.13.* *_cp313 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + license: Python-2.0 + size: 33273132 + timestamp: 1750064035176 + python_site_packages_path: lib/python3.13/site-packages +- conda: https://prefix.dev/conda-forge/osx-64/python-3.12.11-h9ccd52b_0_cpython.conda + sha256: ebda5b5e8e25976013fdd81b5ba253705b076741d02bdc8ab32763f2afb2c81b + md5: 06049132ecd09d0c1dc3d54d93cf1d5d + depends: + - __osx >=10.13 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - liblzma >=5.8.1,<6.0a0 + - libsqlite >=3.50.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 13571569 + timestamp: 1749049058713 - conda: https://prefix.dev/conda-forge/osx-64/python-3.12.9-h9ccd52b_0_cpython.conda sha256: 17d28d74c91b8a6f7844e6dbeec48cc663a81567ecad88ab032c8422d661be7b md5: 0caa16f85e8ed238ab1430691dff1644 @@ -2748,6 +4383,50 @@ packages: license: Python-2.0 size: 12947786 timestamp: 1739520092196 +- conda: https://prefix.dev/conda-forge/osx-arm64/python-3.13.5-hf3f3da0_102_cp313.conda + build_number: 102 + sha256: ee1b09fb5563be8509bb9b29b2b436a0af75488b5f1fa6bcd93fe0fba597d13f + md5: 123b7f04e7b8d6fc206cf2d3466f8a4b + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - liblzma >=5.8.1,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.50.1,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.0,<4.0a0 + - python_abi 3.13.* *_cp313 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + license: Python-2.0 + size: 12931515 + timestamp: 1750062475020 + python_site_packages_path: lib/python3.13/site-packages +- conda: https://prefix.dev/conda-forge/win-64/python-3.12.11-h3f84c4b_0_cpython.conda + sha256: b69412e64971b5da3ced0fc36f05d0eacc9393f2084c6f92b8f28ee068d83e2e + md5: 6aa5e62df29efa6319542ae5025f4376 + depends: + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - liblzma >=5.8.1,<6.0a0 + - libsqlite >=3.50.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 15829289 + timestamp: 1749047682640 - conda: https://prefix.dev/conda-forge/win-64/python-3.12.9-h3f84c4b_0_cpython.conda sha256: 972ef8c58bb1efd058ec70fa957f673e5ad7298d05e501769359f49ae26c7065 md5: f01cb4695ac632a3530200455e31cec5 @@ -2769,6 +4448,16 @@ packages: license: Python-2.0 size: 15963997 timestamp: 1739519811306 +- conda: https://prefix.dev/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + sha256: f3ceef02ac164a8d3a080d0d32f8e2ebe10dd29e3a685d240e38b3599e146320 + md5: 2cf4264fffb9e6eff6031c5b6884d61c + depends: + - python >=3.7 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222742 + timestamp: 1709299922152 - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.12-5_cp312.conda build_number: 5 sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 @@ -2779,6 +4468,26 @@ packages: license_family: BSD size: 6238 timestamp: 1723823388266 +- conda: https://prefix.dev/conda-forge/noarch/python_abi-3.12-8_cp312.conda + build_number: 8 + sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 + md5: c3efd25ac4d74b1584d2f7a57195ddf1 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6958 + timestamp: 1752805918820 +- conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313.conda + build_number: 8 + sha256: 210bffe7b121e651419cb196a2a63687b087497595c9be9d20ebe97dd06060a7 + md5: 94305520c52a4aa3f6c2b1ff6008d9f8 + constrains: + - python 3.13.* *_cp313 + license: BSD-3-Clause + license_family: BSD + size: 7002 + timestamp: 1752805902938 - conda: https://prefix.dev/conda-forge/osx-64/python_abi-3.12-5_cp312.conda build_number: 5 sha256: 4da26c7508d5bc5d8621e84dc510284402239df56aab3587a7d217de9d3c806d @@ -2861,6 +4570,106 @@ packages: license_family: MIT size: 181734 timestamp: 1737455207230 +- conda: crates/rattler-bin + name: rattler + version: 0.1.0 + build: hbf21a9e_0 + subdir: linux-64 + constrains: + - __glibc >=2.17 + license: BSD-3-Clause + input: + hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler-bin + name: rattler + version: 0.1.0 + build: hbf21a9e_0 + subdir: osx-64 + constrains: + - __osx >=10.13 + license: BSD-3-Clause + input: + hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler-bin + name: rattler + version: 0.1.0 + build: hbf21a9e_0 + subdir: osx-arm64 + constrains: + - __osx >=11.0 + license: BSD-3-Clause + input: + hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler-bin + name: rattler + version: 0.1.0 + build: hbf21a9e_0 + subdir: win-64 + license: BSD-3-Clause + input: + hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler_index + name: rattler_index + version: 0.24.10 + build: hbf21a9e_0 + subdir: linux-64 + constrains: + - __glibc >=2.17 + license: BSD-3-Clause + input: + hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler_index + name: rattler_index + version: 0.24.10 + build: hbf21a9e_0 + subdir: osx-64 + constrains: + - __osx >=10.13 + license: BSD-3-Clause + input: + hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler_index + name: rattler_index + version: 0.24.10 + build: hbf21a9e_0 + subdir: osx-arm64 + constrains: + - __osx >=11.0 + license: BSD-3-Clause + input: + hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + globs: + - ../../**/Cargo.toml + - Cargo.toml +- conda: crates/rattler_index + name: rattler_index + version: 0.24.10 + build: hbf21a9e_0 + subdir: win-64 + license: BSD-3-Clause + input: + hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + globs: + - ../../**/Cargo.toml + - Cargo.toml - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c md5: 283b96675859b20a825f8fa30f311446 @@ -2909,6 +4718,20 @@ packages: license_family: MIT size: 176444 timestamp: 1693427792263 +- conda: https://prefix.dev/conda-forge/linux-64/ruamel.yaml-0.17.17-py313h536fd9c_3.conda + sha256: 01c354caee3cb50ba9c888006bbdbdf06b8e30bd4d72eb50a65acd7e9d541251 + md5: 267f55b507598cd08f95c268e063443a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - ruamel.yaml.clib >=0.1.2 + - setuptools + license: MIT + license_family: MIT + size: 253461 + timestamp: 1728738926914 - conda: https://prefix.dev/conda-forge/linux-64/ruamel.yaml-0.18.10-py312h66e93f0_0.conda sha256: cd8ed10671111f15245cebadc06b88d6f5fc91f1f7f92456daa568e9d9f5bc42 md5: 5260b7fb19694ee5bc4ed0ee7a2a769f @@ -2922,6 +4745,19 @@ packages: license_family: MIT size: 267560 timestamp: 1736248154294 +- conda: https://prefix.dev/conda-forge/osx-64/ruamel.yaml-0.17.17-py312h3d0f464_3.conda + sha256: 6596c84c899658158a1824ab89e149d9e1ee028f7a4f114adb1dc9fcfa272ad4 + md5: 8612c431ff50f397d6e980677578c37f + depends: + - __osx >=10.13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ruamel.yaml.clib >=0.1.2 + - setuptools + license: MIT + license_family: MIT + size: 251341 + timestamp: 1728738935612 - conda: https://prefix.dev/conda-forge/osx-64/ruamel.yaml-0.18.10-py312h01d7ebd_0.conda sha256: 1d4ce5cfa530c3971b95a41fa1b1a952cd934f2ed1f34ac22736eece17727ab2 md5: e6fb89d650ea648b6be3bf2fa5026523 @@ -2934,6 +4770,20 @@ packages: license_family: MIT size: 267714 timestamp: 1736248265318 +- conda: https://prefix.dev/conda-forge/osx-arm64/ruamel.yaml-0.17.17-py313h63a2874_3.conda + sha256: 29a8ba2698ec1cc6510b13db0d4a44217201a5152640fb663795308f7969caf7 + md5: 5688dab7282d9038886dfa57e445eeb2 + depends: + - __osx >=11.0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + - ruamel.yaml.clib >=0.1.2 + - setuptools + license: MIT + license_family: MIT + size: 253774 + timestamp: 1728739049255 - conda: https://prefix.dev/conda-forge/osx-arm64/ruamel.yaml-0.18.10-py312hea69d52_0.conda sha256: bb946732dec6943a56650d2690e37d7eed0fc7ffb9d616ae09000b52894f8baf md5: 29a66b19662a643786715a8885c645d0 @@ -2947,6 +4797,21 @@ packages: license_family: MIT size: 268631 timestamp: 1736248245357 +- conda: https://prefix.dev/conda-forge/win-64/ruamel.yaml-0.17.17-py312h4389bb4_3.conda + sha256: 882e831ba2ac4db5e0f19cfbd6a69a0714f7ef90accfdb7afdf23ea9c9c391d8 + md5: 12b2c410e9016b6c72641765a1141192 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ruamel.yaml.clib >=0.1.2 + - setuptools + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: MIT + license_family: MIT + size: 247721 + timestamp: 1728739206454 - conda: https://prefix.dev/conda-forge/win-64/ruamel.yaml-0.18.10-py312h4389bb4_0.conda sha256: 75f0ca8e9d1a025aa11115d0d756710c375b4392fcc711c3a798370b268f5a9a md5: a5d15fd8ca166ab55225e6cfa8cb51f0 @@ -2973,6 +4838,18 @@ packages: license_family: MIT size: 145481 timestamp: 1728724626666 +- conda: https://prefix.dev/conda-forge/linux-64/ruamel.yaml.clib-0.2.8-py313h536fd9c_1.conda + sha256: ef739ff0b07df6406efcb49eed327d931d4dfa6072f98def6a0ae700e584a338 + md5: d3400df9c9d0b58368bc0c0fc2591c39 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + size: 144267 + timestamp: 1728724587572 - conda: https://prefix.dev/conda-forge/osx-64/ruamel.yaml.clib-0.2.8-py312h3d0f464_1.conda sha256: b5ddb73db7ca3d4d8780af1761efb97a5f555ae489f287a91367624d4425f498 md5: f4c0464f98dabcd65064e89991c3c9c2 @@ -2996,6 +4873,18 @@ packages: license_family: MIT size: 117121 timestamp: 1728724705098 +- conda: https://prefix.dev/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.8-py313h63a2874_1.conda + sha256: 8ed7448178b423dbd59cdea422b1fb732c16beacff2cc70f727eff1afd307896 + md5: 34ad7f96e9e4bae5f9a88d0fb04ad557 + depends: + - __osx >=11.0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + size: 115973 + timestamp: 1728724684349 - conda: https://prefix.dev/conda-forge/win-64/ruamel.yaml.clib-0.2.8-py312h4389bb4_1.conda sha256: d5583406ea6d17391294da0a6dadf9a22aad732d1f658f2d6d12fc50b968c0fa md5: 5758e70a80936d7527f70196685c6695 @@ -3165,6 +5054,17 @@ packages: license_family: MIT size: 37041309 timestamp: 1751057508252 +- conda: https://prefix.dev/conda-forge/linux-64/s2n-1.5.23-h8e187f5_0.conda + sha256: 016fe83763bc837beb205732411583179e2aac1cdef40225d4ad5eeb1bc7b837 + md5: edd15d7a5914dc1d87617a2b7c582d23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - openssl >=3.5.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 383097 + timestamp: 1753407970803 - conda: https://prefix.dev/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda sha256: e0778e4f276e9a81b51c56f51ec22a27b4d8fc955abc0be77ad09ca9bea06bb9 md5: 8f28e299c11afdd79e0ec1e279dcdc52 @@ -3174,6 +5074,15 @@ packages: license_family: MIT size: 775598 timestamp: 1736512753595 +- conda: https://prefix.dev/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + sha256: 972560fcf9657058e3e1f97186cc94389144b46dbdf58c807ce62e83f977e863 + md5: 4de79c071274a53dcaf2a8c749d1499e + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 748788 + timestamp: 1748804951958 - conda: https://prefix.dev/conda-forge/osx-64/sigtool-0.1.3-h88f4db0_0.tar.bz2 sha256: 46fdeadf8f8d725819c4306838cdfd1099cd8fe3e17bd78862a5dfdcd6de61cf md5: fbfb84b9de9a6939cb165c02c69b1865 @@ -3192,6 +5101,16 @@ packages: license_family: MIT size: 210264 timestamp: 1643442231687 +- conda: https://prefix.dev/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d + md5: 3339e3b65d58accf4ca4fb8748ab16b3 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + size: 18455 + timestamp: 1753199211006 - conda: https://prefix.dev/conda-forge/noarch/sysroot_linux-64-2.17-h0157908_18.conda sha256: 69ab5804bdd2e8e493d5709eebff382a72fab3e9af6adf93a237ccf8f7dbd624 md5: 460eba7851277ec1fd80a1a24080787a @@ -3246,6 +5165,17 @@ packages: license_family: BSD size: 3318875 timestamp: 1699202167581 +- conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda + sha256: a84ff687119e6d8752346d1d408d5cf360dee0badd487a472aa8ddedfdc219e1 + md5: a0116df4f4ed05c303811a837d5b39d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + size: 3285204 + timestamp: 1748387766691 - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda sha256: 30412b2e9de4ff82d8c2a7e5d06a15f4f4fef1809a72138b6ccb53a33b26faf5 md5: bf830ba5afc507c6232d4ef0fb1a882d @@ -3255,6 +5185,16 @@ packages: license_family: BSD size: 3270220 timestamp: 1699202389792 +- conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-hf689a15_2.conda + sha256: b24468006a96b71a5f4372205ea7ec4b399b0f2a543541e86f883de54cd623fc + md5: 9864891a6946c2fe037c02fca7392ab4 + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + size: 3259809 + timestamp: 1748387843735 - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 md5: b50a57ba89c32b62428b71a875291c9b @@ -3264,6 +5204,27 @@ packages: license_family: BSD size: 3145523 timestamp: 1699202432999 +- conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + sha256: cb86c522576fa95c6db4c878849af0bccfd3264daf0cc40dd18e7f4a7bfced0e + md5: 7362396c170252e7b7b0c8fb37fe9c78 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + size: 3125538 + timestamp: 1748388189063 +- conda: https://prefix.dev/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda + sha256: e3614b0eb4abcc70d98eae159db59d9b4059ed743ef402081151a948dce95896 + md5: ebd0e761de9aa879a51d22cc721bd095 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: TCL + license_family: BSD + size: 3466348 + timestamp: 1748388121356 - conda: https://prefix.dev/conda-forge/win-64/tk-8.6.13-h5226925_1.conda sha256: 2c4e914f521ccb2718946645108c9bd3fc3216ba69aea20c2c3cedbd8db32bb1 md5: fc048363eb8f03cd1737600a5d08aafe @@ -3335,6 +5296,12 @@ packages: license: LicenseRef-Public-Domain size: 122921 timestamp: 1737119101255 +- conda: https://prefix.dev/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 + md5: 4222072737ccff51314b5ece9c7d6f5a + license: LicenseRef-Public-Domain + size: 122968 + timestamp: 1742727099393 - conda: https://prefix.dev/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda sha256: db8dead3dd30fb1a032737554ce91e2819b43496a0db09927edf01c32b577450 md5: 6797b005cd0f439c4c5c9ac565783700 @@ -3398,6 +5365,28 @@ packages: license_family: MIT size: 17213 timestamp: 1725784449622 +- conda: https://prefix.dev/conda-forge/noarch/urllib3-1.26.19-pyhd8ed1ab_0.conda + sha256: 543ebab5241418a4e0d4d9e356ef13e4361504810a067a01481660bb35eb5643 + md5: 6bb37c314b3cc1515dcf086ffe01c46e + depends: + - brotli-python >=1.0.9 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.7 + license: MIT + license_family: MIT + size: 115125 + timestamp: 1718728467518 +- conda: https://prefix.dev/conda-forge/win-64/vc-14.3-h41ae7f8_31.conda + sha256: cb357591d069a1e6cb74199a8a43a7e3611f72a6caed9faa49dbb3d7a0a98e0b + md5: 28f4ca1e0337d0f27afb8602663c5723 + depends: + - vc14_runtime >=14.44.35208 + track_features: + - vc14 + license: BSD-3-Clause + license_family: BSD + size: 18249 + timestamp: 1753739241465 - conda: https://prefix.dev/conda-forge/win-64/vc-14.3-h5fd82a7_24.conda sha256: 7ce178cf139ccea5079f9c353b3d8415d1d49b0a2f774662c355d3f89163d7b4 md5: 00cf3a61562bd53bd5ea99e6888793d0 @@ -3431,6 +5420,29 @@ packages: license_family: Proprietary size: 756109 timestamp: 1750371459116 +- conda: https://prefix.dev/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda + sha256: af4b4b354b87a9a8d05b8064ff1ea0b47083274f7c30b4eb96bc2312c9b5f08f + md5: 603e41da40a765fd47995faa021da946 + depends: + - ucrt >=10.0.20348.0 + - vcomp14 14.44.35208 h818238b_31 + constrains: + - vs2015_runtime 14.44.35208.* *_31 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + size: 682424 + timestamp: 1753739239305 +- conda: https://prefix.dev/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda + sha256: 67b317b64f47635415776718d25170a9a6f9a1218c0f5a6202bfd687e07b6ea4 + md5: a6b1d5c1fc3cb89f88f7179ee6a9afe3 + depends: + - ucrt >=10.0.20348.0 + constrains: + - vs2015_runtime 14.44.35208.* *_31 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + size: 113963 + timestamp: 1753739198723 - conda: https://prefix.dev/conda-forge/noarch/virtualenv-20.29.2-pyhd8ed1ab_0.conda sha256: c50a4ab0f5f1164230d42a29f12f61ece9c7b102f57ed1c607d2cd7c77e107b5 md5: d8a3ee355d5ecc9ee2565cafba1d3573 @@ -3481,6 +5493,24 @@ packages: license_family: MIT size: 219013 timestamp: 1719460515960 +- conda: https://prefix.dev/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + sha256: f21e63e8f7346f9074fd00ca3b079bd3d2fa4d71f1f89d5b6934bf31446dc2a5 + md5: b68980f2495d096e71c7fd9d7ccf63e6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 32581 + timestamp: 1733231433877 +- conda: https://prefix.dev/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda + sha256: 93807369ab91f230cf9e6e2a237eaa812492fe00face5b38068735858fba954f + md5: 46e441ba871f524e2b067929da3051c2 + depends: + - __win + - python >=3.9 + license: LicenseRef-Public-Domain + size: 9555 + timestamp: 1733130678956 - conda: https://prefix.dev/conda-forge/linux-64/xz-5.6.4-hbcc6ac9_0.conda sha256: 91fc251034fa5199919680aa50299296d89da54b2d066fb6e6a60461c17c0c4a md5: bb511c87804cf7220246a3a6efc45c22 diff --git a/pixi.toml b/pixi.toml index a87140e817..533c4fc70a 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,4 +1,4 @@ -[project] +[workspace] name = "rattler" description = "Rust library to install conda environments" authors = [ @@ -10,6 +10,7 @@ authors = [ channels = ["https://prefix.dev/conda-forge"] platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"] license = "BSD-3-Clause" +preview = ["pixi-build"] [tasks] build = "cargo build" @@ -54,3 +55,35 @@ pre-commit-run = "pre-commit run --all-files --hook-stage manual" lint = { features = [ "lint", ], no-default-feature = true, solve-group = "default" } + +#------------------------------ +# Minio E2E test +#------------------------------ +[feature.minio.dependencies] +minio-server = ">=2025.1.20,<2026" +minio-client = ">=2025.7.21,<2026" +rattler_index = { path = "crates/rattler_index" } +rattler = { path = "crates/rattler-bin" } +nushell = ">=0.106.1,<0.107" + +[feature.minio.tasks] +e2e-s3-minio = "nu scripts/e2e/s3-minio.nu" + +[environments.minio] +features = ["minio"] +no-default-feature = true + +#------------------------------ +# S3 E2E test +#------------------------------ +[feature.s3.dependencies] +awscli = ">=2.28.18,<3" +rattler_index = { path = "crates/rattler_index" } +rattler = { path = "crates/rattler-bin" } + +[feature.s3.tasks] +e2e-s3-aws = "nu scripts/e2e/s3_aws.nu" + +[environments.s3] +features = ["s3"] +no-default-feature = true diff --git a/scripts/e2e/s3-minio.nu b/scripts/e2e/s3-minio.nu new file mode 100644 index 0000000000..ebded42680 --- /dev/null +++ b/scripts/e2e/s3-minio.nu @@ -0,0 +1,85 @@ +#!/usr/bin/env nu + +# Paths +let tmp = ($env.RUNNER_TMP? | default $env.TMP) +let bin_dir = $tmp +let data_dir = $"($tmp)/minio-data" +let log_file = $"($tmp)/minio.log" +let pid_file = $"($tmp)/minio.pid" +let bucket_name = $"tmp-(random int 0..1000000)" + +# Create directories +mkdir $bin_dir $data_dir + +# Credentials +let root_user = ($env.MINIO_ACCESS_KEY? | default "minio") +let root_password = ($env.MINIO_SECRET_KEY? | default "minio123") + +# Start MinIO in background as a job +print "== Starting Minio server..." +let minio_job = job spawn { + with-env { + MINIO_ROOT_USER: $root_user + MINIO_ROOT_PASSWORD: $root_password + } { + ^minio server $data_dir --address ":9000" out+err> $log_file + } +} + +# wait up to 120s (60 × 2s) for MinIO to be ready +if not (seq 0 59 | any {|_| + try { http get http://localhost:9000/minio/health/live | ignore; true } catch { sleep 2sec; false } +}) { + error make {msg: "MinIO failed to start within 120 seconds"} +} +print "Minio server is up and running..." + +# Configure mc client and bucket +print $"== Configuring bucket ($bucket_name)..." +^mc alias set minio http://localhost:9000 $root_user $root_password +^mc mb $"minio/($bucket_name)" +^mc policy set download $"minio/($bucket_name)" + +print "== Upload packages to Minio" +let args = [ + upload s3 + --channel $"s3://($bucket_name)" + --access-key-id $root_user + --secret-access-key $root_password + --region "us-east-1" + --endpoint-url "http://localhost:9000" + --force-path-style true + test-data/packages/empty-0.1.0-h4616a5c_0.conda +] + +^rattler ...$args + +print "== Index the channel ===" +let args = [ + s3 + $"s3://($bucket_name)" + --access-key-id $root_user + --secret-access-key $root_password + --region "us-east-1" + --endpoint-url "http://localhost:9000" + --force-path-style true +] + +^rattler-index ...$args + +print "== Test package can be installed from the channel ===" +let args = [ + create + --dry-run + -c $"s3://($bucket_name)" + empty==0.1.0 +] + +with-env { + AWS_ACCESS_KEY_ID: $root_user + AWS_SECRET_ACCESS_KEY: $root_password + AWS_REGION: "us-east-1" + AWS_ENDPOINT_URL: "http://localhost:9000" +} { + ^rattler ...$args +} \ No newline at end of file From 38ec6072e78e3c90a98955a2ffe0f7e1682d6d50 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 13:48:19 +0200 Subject: [PATCH 22/45] fix: globs of rust backend --- pixi.lock | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/pixi.lock b/pixi.lock index 069cc6ad4e..7bc927b352 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4579,9 +4579,10 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler-bin name: rattler @@ -4592,9 +4593,10 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler-bin name: rattler @@ -4605,9 +4607,10 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler-bin name: rattler @@ -4616,9 +4619,10 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: b0a6ba119b649e512ab740c05435e08d4ecd1110cef747841ca7bf94e11781c2 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -4629,9 +4633,10 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -4642,9 +4647,10 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -4655,9 +4661,10 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -4666,9 +4673,10 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: 64e250ceb1a35b3e6b708cc4e063547bfe0e15883ff740c57f6f80d7d8d1d567 + hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c From f58a0ae670ec90e3ae42baf9952c3a7847501730 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 13:52:00 +0200 Subject: [PATCH 23/45] fix: locked versions --- crates/rattler_index/pixi.toml | 1 + pixi.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/rattler_index/pixi.toml b/crates/rattler_index/pixi.toml index 1b87d1e8e6..5f1fac93eb 100644 --- a/crates/rattler_index/pixi.toml +++ b/crates/rattler_index/pixi.toml @@ -1,5 +1,6 @@ [package] name = "rattler_index" +version = "0.24.10" [package.build.backend] name = "pixi-build-rust" diff --git a/pixi.lock b/pixi.lock index 7bc927b352..95b71c3e2c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4579,7 +4579,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf globs: - ../../Cargo.toml - ../Cargo.toml @@ -4593,7 +4593,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf globs: - ../../Cargo.toml - ../Cargo.toml @@ -4607,7 +4607,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf globs: - ../../Cargo.toml - ../Cargo.toml @@ -4619,7 +4619,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf globs: - ../../Cargo.toml - ../Cargo.toml @@ -4633,7 +4633,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b + hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4647,7 +4647,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b + hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4661,7 +4661,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b + hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4673,7 +4673,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: acd59c412719b0b08d85a3b15a830d5cfc3f52ad11b11fe4f41584ee9bde022b + hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 globs: - ../../Cargo.toml - ../Cargo.toml From 4ef1fbaa5a6092e3ada8e430ee3cfcccdc273ae8 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 13:58:14 +0200 Subject: [PATCH 24/45] test: enable sccache and fix sha --- .github/workflows/e2e-s3-tests.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 67faa4e9b2..ef1615f0c1 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -1,6 +1,6 @@ on: push: - branches: [main] + branches: [ main ] pull_request: paths: - crates/rattler-bin/** @@ -27,15 +27,20 @@ jobs: name: E2E Upload/Index/Download [Minio] runs-on: ubuntu-latest + env: + # Enable sccache. + # + # This environment variable is picked up by pixi build which will then + # set up the rust build using sccache. + SCCACHE_GHA_ENABLED: "true" + steps: - name: Checkout source code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: submodules: recursive - - uses: mozilla-actions/sccache-action@v0.0.9 - - - uses: prefix-dev/setup-pixi@v0.9.0 + - uses: prefix-dev/setup-pixi@fef5c9568ca6c4ff7707bf840ab0692ba3f08293 # v0.9.0 with: environments: minio activate-environment: minio @@ -83,7 +88,7 @@ jobs: aws s3api put-bucket-lifecycle-configuration --bucket "${BUCKET}" --lifecycle-configuration '{ "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] }' - + - name: Build rattler binaries run: | cargo build --bin rattler --release From 5f5b40384b9fadef22faf6dbb052b24c3cab2d6b Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 14:37:19 +0200 Subject: [PATCH 25/45] fix: aws test script --- .github/workflows/e2e-s3-tests.yml | 27 ++++++++++++++---- pixi.lock | 4 +++ pixi.toml | 7 ++++- scripts/e2e/s3-aws.nu | 44 ++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 scripts/e2e/s3-aws.nu diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index ef1615f0c1..9c4766c25e 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -18,7 +18,6 @@ concurrency: env: RUST_LOG: info RUST_BACKTRACE: 1 - RUSTFLAGS: "-D warnings" CARGO_TERM_COLOR: always DEFAULT_FEATURES: s3 @@ -57,6 +56,11 @@ jobs: contents: read env: + # Enable sccache. + # + # This environment variable is picked up by pixi build which will then + # set up the rust build using sccache. + SCCACHE_GHA_ENABLED: "true" AWS_REGION: eu-west-1 BUCKET: tmp-${{ github.repository_owner }}-${{ github.event.repository.name }}-${{ github.run_id }}-${{ github.run_attempt }} @@ -66,16 +70,29 @@ jobs: with: submodules: recursive - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - name: Configure AWS (OIDC) uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: aws-region: ${{ env.AWS_REGION }} role-to-assume: arn:aws:iam::239378270001:role/conda-rattler-e2e-test + - uses: prefix-dev/setup-pixi@fef5c9568ca6c4ff7707bf840ab0692ba3f08293 # v0.9.0 + with: + environments: s3 + activate-environment: s3 + + - run: pixi run e2e-s3-aws + + steps: + - name: Checkout source code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: Generate unique bucket name id: bucket run: | diff --git a/pixi.lock b/pixi.lock index 95b71c3e2c..8e91eee792 100644 --- a/pixi.lock +++ b/pixi.lock @@ -484,6 +484,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - conda: https://prefix.dev/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://prefix.dev/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://prefix.dev/conda-forge/linux-64/nushell-0.106.1-hb0b18c3_0.conda - conda: https://prefix.dev/conda-forge/linux-64/openssl-3.5.2-h26f9b46_0.conda - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda @@ -532,6 +533,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-64/libsqlite-3.50.4-h39a8b3b_0.conda - conda: https://prefix.dev/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://prefix.dev/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda + - conda: https://prefix.dev/conda-forge/osx-64/nushell-0.106.1-h088d292_0.conda - conda: https://prefix.dev/conda-forge/osx-64/openssl-3.5.2-h6e31bce_0.conda - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda @@ -581,6 +583,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/nushell-0.106.1-h1b39ca6_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/openssl-3.5.2-he92f556_0.conda - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda @@ -626,6 +629,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://prefix.dev/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda - conda: https://prefix.dev/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda + - conda: https://prefix.dev/conda-forge/win-64/nushell-0.106.1-hdab3696_0.conda - conda: https://prefix.dev/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda diff --git a/pixi.toml b/pixi.toml index 533c4fc70a..86f8751b28 100644 --- a/pixi.toml +++ b/pixi.toml @@ -12,6 +12,10 @@ platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"] license = "BSD-3-Clause" preview = ["pixi-build"] +[workspace.build-variants] +# Use the same Rust version everywhere +rust_compiler_version = ["1.88.0"] + [tasks] build = "cargo build" fmt = "cargo fmt --all" @@ -80,9 +84,10 @@ no-default-feature = true awscli = ">=2.28.18,<3" rattler_index = { path = "crates/rattler_index" } rattler = { path = "crates/rattler-bin" } +nushell = ">=0.106.1,<0.107" [feature.s3.tasks] -e2e-s3-aws = "nu scripts/e2e/s3_aws.nu" +e2e-s3-aws = "nu scripts/e2e/s3-aws.nu" [environments.s3] features = ["s3"] diff --git a/scripts/e2e/s3-aws.nu b/scripts/e2e/s3-aws.nu new file mode 100644 index 0000000000..4b9248a58d --- /dev/null +++ b/scripts/e2e/s3-aws.nu @@ -0,0 +1,44 @@ +#!/usr/bin/env nu + +let bucket_name = ($env.BUCKET? | default $"tmp-conda-rattler-(random int 0..1000000)") +let region = ($env.AWS_REGION? | default "eu-west-1") + +def run [desc: string, cmd: closure] { + print $"== ($desc)" + do $cmd | ignore + let code = ($env.LAST_EXIT_CODE? | default 0) + if $code != 0 { + print $"WARN: ($desc) failed \(exit=($code)\)" + false + } else { true } +} + + +def bucket_exists [] { + (do { ^aws s3api head-bucket --bucket $bucket_name } | complete).exit_code == 0 +} + +# --- steps (don’t abort on failure) --- +let test_ok = (run $"Create bucket ($bucket_name)" { + ^aws s3api create-bucket --bucket $bucket_name --create-bucket-configuration $"LocationConstraint=($region)" +}) and (run "Set lifecycle (1 day)" { + ^aws s3api put-bucket-lifecycle-configuration --bucket $bucket_name --lifecycle-configuration '{ "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] }' +}) and (run "Upload package" { + ^rattler upload s3 --channel $"s3://($bucket_name)" test-data/packages/empty-0.1.0-h4616a5c_0.conda +}) and (run "Index channel" { + ^rattler-index s3 $"s3://($bucket_name)" +}) and (run "Dry-run install" { + ^rattler create --dry-run -c $"s3://($bucket_name)" empty==0.1.0 +}) + +# --- cleanup always attempted --- +if (bucket_exists) { + print "== Cleanup: remove bucket and all its contents" + (do { ^aws s3 rm $"s3://($bucket_name)" --recursive } | complete) | ignore + (do { ^aws s3 rb $"s3://($bucket_name)" } | complete) | ignore +} else { + print "== Cleanup: bucket did not exist (skip)" +} + +# --- exit non-zero if any step failed --- +if not $test_ok { exit 1 } From 6daf82f72c9981c8abffe7914bce0b164b590063 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 14:40:01 +0200 Subject: [PATCH 26/45] fix: broken workflow --- .github/workflows/e2e-s3-tests.yml | 71 +++--------------------------- 1 file changed, 6 insertions(+), 65 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 9c4766c25e..283243bd26 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -13,7 +13,7 @@ name: E2E S3 Tests concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: false env: RUST_LOG: info @@ -61,7 +61,11 @@ jobs: # This environment variable is picked up by pixi build which will then # set up the rust build using sccache. SCCACHE_GHA_ENABLED: "true" + + # The region to create buckets it. AWS_REGION: eu-west-1 + + # The name of the test bucket to create. BUCKET: tmp-${{ github.repository_owner }}-${{ github.event.repository.name }}-${{ github.run_id }}-${{ github.run_attempt }} steps: @@ -81,67 +85,4 @@ jobs: environments: s3 activate-environment: s3 - - run: pixi run e2e-s3-aws - - steps: - - name: Checkout source code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - submodules: recursive - - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 - with: - save-if: ${{ github.ref == 'refs/heads/main' }} - - - name: Generate unique bucket name - id: bucket - run: | - # Create a unique bucket name using repository owner, name, run ID, and attempt number - aws s3api create-bucket \ - --bucket "${BUCKET}" \ - --create-bucket-configuration LocationConstraint=${AWS_REGION} - - # 1-day auto-expire objects (safety net to ensure we dont retain any files) - aws s3api put-bucket-lifecycle-configuration --bucket "${BUCKET}" --lifecycle-configuration '{ - "Rules":[{"ID":"ttl-1d","Status":"Enabled","Expiration":{"Days":1},"Filter":{"Prefix":""}}] - }' - - - name: Build rattler binaries - run: | - cargo build --bin rattler --release - cargo build --bin rattler-index --release - - - name: Run E2E AWS S3 workflow test - run: | - set -e - - echo "=== Step 1: Upload package to AWS S3 ===" - ./target/release/rattler upload s3 \ - --channel s3://${BUCKET} \ - test-data/packages/empty-0.1.0-h4616a5c_0.conda - - echo "=== Step 2: Index the channel ===" - ./target/release/rattler-index s3 s3://${BUCKET} - - echo "=== Step 3: Test package discovery with dry-run ===" - ./target/release/rattler create \ - --dry-run \ - -c s3://${BUCKET} \ - empty==0.1.0 - - echo "=== AWS S3 E2E test completed successfully ===" - - - name: Debug bucket contents on failure - if: failure() - run: | - echo "=== AWS S3 bucket contents ===" - aws s3 ls s3://${BUCKET} --recursive || true - - - name: Cleanup AWS S3 bucket - if: always() - run: | - # Remove all objects first - aws s3 rm s3://${BUCKET} --recursive || true - # Then delete the bucket - aws s3 rb s3://${BUCKET} || true - echo "AWS S3 test cleanup completed" + - run: pixi run e2e-s3-aws \ No newline at end of file From a188046b16f8cdb9f91558ce20aa832cf6f22286 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 14:42:57 +0200 Subject: [PATCH 27/45] fix: update pixi.lock --- pixi.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pixi.lock b/pixi.lock index 8e91eee792..0d57432150 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4577,7 +4577,7 @@ packages: - conda: crates/rattler-bin name: rattler version: 0.1.0 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: linux-64 constrains: - __glibc >=2.17 @@ -4591,7 +4591,7 @@ packages: - conda: crates/rattler-bin name: rattler version: 0.1.0 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: osx-64 constrains: - __osx >=10.13 @@ -4605,7 +4605,7 @@ packages: - conda: crates/rattler-bin name: rattler version: 0.1.0 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: osx-arm64 constrains: - __osx >=11.0 @@ -4619,7 +4619,7 @@ packages: - conda: crates/rattler-bin name: rattler version: 0.1.0 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: win-64 license: BSD-3-Clause input: @@ -4631,7 +4631,7 @@ packages: - conda: crates/rattler_index name: rattler_index version: 0.24.10 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: linux-64 constrains: - __glibc >=2.17 @@ -4645,7 +4645,7 @@ packages: - conda: crates/rattler_index name: rattler_index version: 0.24.10 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: osx-64 constrains: - __osx >=10.13 @@ -4659,7 +4659,7 @@ packages: - conda: crates/rattler_index name: rattler_index version: 0.24.10 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: osx-arm64 constrains: - __osx >=11.0 @@ -4673,7 +4673,7 @@ packages: - conda: crates/rattler_index name: rattler_index version: 0.24.10 - build: hbf21a9e_0 + build: ha7ad5ef_0 subdir: win-64 license: BSD-3-Clause input: From 430f8a740a7de2193d295416dea2ab247ddd2d7b Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:43:18 +0200 Subject: [PATCH 28/45] fix: streamline s3 access --- .github/workflows/e2e-s3-tests.yml | 2 - Cargo.lock | 19 ++- Cargo.toml | 2 + crates/rattler_index/Cargo.toml | 11 +- crates/rattler_index/src/lib.rs | 104 ++-------------- crates/rattler_index/src/main.rs | 63 +++++----- crates/rattler_s3/Cargo.toml | 21 ++++ crates/rattler_s3/src/clap.rs | 46 +++++++ crates/rattler_s3/src/lib.rs | 152 ++++++++++++++++++++++++ crates/rattler_upload/Cargo.toml | 5 +- crates/rattler_upload/src/lib.rs | 8 +- crates/rattler_upload/src/upload/opt.rs | 23 +--- crates/rattler_upload/src/upload/s3.rs | 102 ++++------------ pixi.lock | 8 +- scripts/e2e/s3-minio.nu | 60 +++++----- 15 files changed, 333 insertions(+), 293 deletions(-) create mode 100644 crates/rattler_s3/Cargo.toml create mode 100644 crates/rattler_s3/src/clap.rs create mode 100644 crates/rattler_s3/src/lib.rs diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 283243bd26..ab428c401a 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -42,7 +42,6 @@ jobs: - uses: prefix-dev/setup-pixi@fef5c9568ca6c4ff7707bf840ab0692ba3f08293 # v0.9.0 with: environments: minio - activate-environment: minio - run: pixi run e2e-s3-minio @@ -83,6 +82,5 @@ jobs: - uses: prefix-dev/setup-pixi@fef5c9568ca6c4ff7707bf840ab0692ba3f08293 # v0.9.0 with: environments: s3 - activate-environment: s3 - run: pixi run e2e-s3-aws \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index fc90532b0f..5ec72b441c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4632,8 +4632,6 @@ name = "rattler_index" version = "0.24.10" dependencies = [ "anyhow", - "aws-config", - "aws-sdk-s3", "bytes", "chrono", "clap", @@ -4649,6 +4647,7 @@ dependencies = [ "rattler_digest", "rattler_networking", "rattler_package_streaming", + "rattler_s3", "reqwest", "rmp-serde", "serde", @@ -4899,6 +4898,19 @@ dependencies = [ "zstd", ] +[[package]] +name = "rattler_s3" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-credential-types", + "aws-sdk-s3", + "clap", + "rattler_networking", + "thiserror 2.0.16", + "url", +] + [[package]] name = "rattler_sandbox" version = "0.1.10" @@ -4962,8 +4974,6 @@ dependencies = [ name = "rattler_upload" version = "0.2.0" dependencies = [ - "aws-config", - "aws-sdk-s3", "base64 0.22.1", "clap", "fs-err", @@ -4977,6 +4987,7 @@ dependencies = [ "rattler_networking", "rattler_package_streaming", "rattler_redaction", + "rattler_s3", "rattler_solve", "reqwest", "reqwest-middleware", diff --git a/Cargo.toml b/Cargo.toml index 242ea3aa83..207b9d8ce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ glob = "0.3.2" google-cloud-auth = { version = "0.22.0", default-features = false } aws-config = { version = "=1.5.18", default-features = false } aws-sdk-s3 = { version = "1.85.0", default-features = false } +aws-credential-types = { version = "1.2.5", default-features = false } hex = "0.4.3" hex-literal = "1.0.0" http = "1.3" @@ -193,6 +194,7 @@ rattler_pty = { path = "crates/rattler_pty", version = "=0.2.6", default-feature rattler_redaction = { path = "crates/rattler_redaction", version = "=0.1.12", default-features = false } rattler_package_streaming = { path = "crates/rattler_package_streaming", version = "=0.23.1", default-features = false } rattler_repodata_gateway = { path = "crates/rattler_repodata_gateway", version = "=0.24.1", default-features = false } +rattler_s3 = { path = "crates/rattler_s3", version = "=0.1.0", default-features = false } rattler_sandbox = { path = "crates/rattler_sandbox", version = "=0.1.10", default-features = false } rattler_shell = { path = "crates/rattler_shell", version = "=0.24.9", default-features = false } rattler_solve = { path = "crates/rattler_solve", version = "=3.0.1", default-features = false } diff --git a/crates/rattler_index/Cargo.toml b/crates/rattler_index/Cargo.toml index 014367c72b..adf501ce2c 100644 --- a/crates/rattler_index/Cargo.toml +++ b/crates/rattler_index/Cargo.toml @@ -32,16 +32,6 @@ path = "src/main.rs" [dependencies] anyhow = { workspace = true } -aws-config = { workspace = true, features = [ - "rt-tokio", - "rustls", - "sso", - "credentials-process", -] } -aws-sdk-s3 = { workspace = true, features = [ - "rt-tokio", - "rustls", -] } bytes = { workspace = true } chrono = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } @@ -62,6 +52,7 @@ rattler_networking = { workspace = true, default-features = false, features = [ rattler_conda_types = { workspace = true, default-features = false } rattler_digest = { workspace = true, default-features = false } rattler_package_streaming = { workspace = true, default-features = false } +rattler_s3 = { workspace = true, features = ["clap"] } reqwest = { workspace = true, default-features = false, features = [ "http2", "macos-system-configuration", diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index 05cbcd0758..082c6d4f99 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -11,8 +11,6 @@ use std::{ }; use anyhow::{Context, Result}; -use aws_config::{meta::region::RegionProviderChain, BehaviorVersion}; -use aws_sdk_s3::config::{Credentials, ProvideCredentials}; use bytes::buf::Buf; use fs_err::{self as fs}; use futures::{stream::FuturesUnordered, StreamExt}; @@ -20,7 +18,7 @@ use fxhash::FxHashMap; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use opendal::{ layers::RetryLayer, - services::{FsConfig, S3Config, S3}, + services::{FsConfig, S3Config}, Configurator, ErrorKind, Operator, }; use rattler_conda_types::{ @@ -29,11 +27,11 @@ use rattler_conda_types::{ ShardedSubdirInfo, }; use rattler_digest::Sha256Hash; -use rattler_networking::{Authentication, AuthenticationStorage}; use rattler_package_streaming::{ read, seek::{self, stream_conda_content}, }; +use rattler_s3::ResolvedS3Credentials; use serde::Serialize; use sha2::{Digest, Sha256}; use tokio::sync::Semaphore; @@ -611,29 +609,10 @@ pub async fn index_fs( pub struct IndexS3Config { /// The channel to index. pub channel: Url, - /// The region of the S3 bucket. If not set, the region will be auto - /// detected from the endpoint URL. - pub region: Option, - /// The endpoint URL of the S3 bucket. If not set: - /// - The endpoint will be loaded from `AWS_ENDPOINT_URL` environment - /// variable. - /// - If that is not set, the default endpoint will be used (`https://s3.amazonaws.com`). - pub endpoint_url: Option, + /// The resolved credentials to use for S3 access. + pub credentials: ResolvedS3Credentials, /// Whether to force path style for the S3 bucket. pub force_path_style: Option, - /// The access key ID for the S3 bucket. - /// If not set, the access key will be loaded from the environment, if the - /// access key was still not loaded, the authentication storage will be - /// queried. - pub access_key_id: Option, - /// The secret access key for the S3 bucket. - /// If not set, the access key will be loaded from the environment, if the - /// access key was still not loaded, the authentication storage will be - /// queried. - pub secret_access_key: Option, - /// The session token for the S3 bucket. - /// If not set, the authentication storage will be queried. - pub session_token: Option, /// The target platform to index. pub target_platform: Option, /// The path to a repodata patch to apply to the index. @@ -655,12 +634,8 @@ pub struct IndexS3Config { pub async fn index_s3( IndexS3Config { channel, - region, - endpoint_url, + credentials, force_path_style, - access_key_id, - secret_access_key, - session_token, target_platform, repodata_patch, write_zst, @@ -678,54 +653,15 @@ pub async fn index_s3( .ok_or(anyhow::anyhow!("No bucket in S3 URL"))? .to_string(); - // Determine region and endpoint URL. - let endpoint = endpoint_url - .map(|url| url.to_string()) - .or_else(|| std::env::var("AWS_ENDPOINT_URL").ok()) - .unwrap_or_else(|| String::from("https://s3.amazonaws.com")); - - let mut region = region; - if region.is_none() { - // Try to use the AWS SDK to determine the region. - let region_provider = RegionProviderChain::default_provider(); - region = region_provider.region().await.map(|r| r.to_string()); - } - if region.is_none() { - // If no region is provided, we try to detect it from the endpoint URL. - region = S3::detect_region(&endpoint, &s3_config.bucket).await; - } - s3_config.region = region; - s3_config.endpoint = Some(endpoint); + s3_config.region = Some(credentials.region); + s3_config.endpoint = Some(credentials.endpoint_url.to_string()); + s3_config.secret_access_key = Some(credentials.secret_access_key); + s3_config.access_key_id = Some(credentials.access_key_id); + s3_config.session_token = credentials.session_token; // How to access the S3 bucket. s3_config.enable_virtual_host_style = force_path_style.is_none_or(|x| !x); - // Use credentials from the CLI if they are provided. - if let (Some(access_key_id), Some(secret_access_key)) = (access_key_id, secret_access_key) { - s3_config.secret_access_key = Some(secret_access_key); - s3_config.access_key_id = Some(access_key_id); - s3_config.session_token = session_token; - } else if let Some((access_key_id, secret_access_key, session_token)) = - load_s3_credentials_from_auth_storage(channel.clone())? - { - // Use the credentials from the authentication storage if they are available. - s3_config.access_key_id = Some(access_key_id); - s3_config.secret_access_key = Some(secret_access_key); - s3_config.session_token = session_token; - } else { - let config = aws_config::load_defaults(BehaviorVersion::latest()).await; - let Some(credentials_provider) = config.credentials_provider() else { - return Err(anyhow::anyhow!("No AWS credentials provider found",)); - }; - let credentials: Credentials = credentials_provider - .provide_credentials() - .await - .context("failed to determine AWS credentials")?; - s3_config.access_key_id = Some(credentials.access_key_id().to_string()); - s3_config.secret_access_key = Some(credentials.secret_access_key().to_string()); - s3_config.session_token = credentials.session_token().map(ToString::to_string); - } - index( target_platform, s3_config, @@ -739,26 +675,6 @@ pub async fn index_s3( .await } -fn load_s3_credentials_from_auth_storage( - channel: Url, -) -> anyhow::Result)>> { - let auth_storage = AuthenticationStorage::from_env_and_defaults()?; - let auth = auth_storage.get_by_url(channel)?; - if let ( - _, - Some(Authentication::S3Credentials { - access_key_id, - secret_access_key, - session_token, - }), - ) = auth - { - Ok(Some((access_key_id, secret_access_key, session_token))) - } else { - Ok(None) - } -} - /// Create a new `repodata.json` for all packages in the given configurator's /// root. If `target_platform` is `Some`, only that specific subdir is indexed. /// Otherwise indexes all subdirs and creates a `repodata.json` for each. diff --git a/crates/rattler_index/src/main.rs b/crates/rattler_index/src/main.rs index c148cc71d2..33f8ae1a8f 100644 --- a/crates/rattler_index/src/main.rs +++ b/crates/rattler_index/src/main.rs @@ -6,6 +6,8 @@ use clap_verbosity_flag::Verbosity; use rattler_conda_types::Platform; use rattler_config::config::concurrency::default_max_concurrent_solves; use rattler_index::{index_fs, index_s3, IndexFsConfig, IndexS3Config}; +use rattler_networking::AuthenticationStorage; +use rattler_s3::S3Credentials; use url::Url; fn parse_s3_url(value: &str) -> Result { @@ -38,7 +40,8 @@ struct Cli { write_shards: Option, /// Whether to force the re-indexing of all packages. - /// Note that this will create a new repodata.json instead of updating the existing one. + /// Note that this will create a new repodata.json instead of updating the + /// existing one. #[arg(short, long, default_value = "false", global = true)] force: bool, @@ -52,8 +55,8 @@ struct Cli { #[arg(long, global = true)] target_platform: Option, - /// The name of the conda package (expected to be in the `noarch` subdir) that should be used for repodata patching. - /// For more information, see `https://prefix.dev/blog/repodata_patching`. + /// The name of the conda package (expected to be in the `noarch` subdir) + /// that should be used for repodata patching. For more information, see `https://prefix.dev/blog/repodata_patching`. #[arg(long, global = true)] repodata_patch: Option, @@ -81,33 +84,17 @@ enum Commands { #[arg(value_parser = parse_s3_url)] channel: Url, - /// The endpoint URL of the S3 backend - #[arg(long, env = "S3_ENDPOINT_URL")] - endpoint_url: Option, - - /// The region of the S3 backend - #[arg(long, env = "S3_REGION")] - region: Option, + #[clap(flatten)] + credentials: rattler_s3::clap::S3CredentialsOpts, /// Whether to use path-style S3 URLs #[arg(long, env = "S3_FORCE_PATH_STYLE")] force_path_style: Option, - - /// The access key ID for the S3 bucket. - #[arg(long, env = "S3_ACCESS_KEY_ID", requires_all = ["secret_access_key"])] - access_key_id: Option, - - /// The secret access key for the S3 bucket. - #[arg(long, env = "S3_SECRET_ACCESS_KEY", requires_all = ["access_key_id"])] - secret_access_key: Option, - - /// The session token for the S3 bucket. - #[arg(long, env = "S3_SESSION_TOKEN", requires_all = ["access_key_id", "secret_access_key"])] - session_token: Option, }, } -/// The configuration type for rattler-index - just extends rattler config and can load the same TOML files as pixi. +/// The configuration type for rattler-index - just extends rattler config and +/// can load the same TOML files as pixi. pub type Config = rattler_config::config::ConfigBase<()>; /// Entry point of the `rattler-index` cli. @@ -148,29 +135,33 @@ async fn main() -> anyhow::Result<()> { } Commands::S3 { channel, - region, - endpoint_url, + mut credentials, force_path_style, - access_key_id, - secret_access_key, - session_token, } => { let bucket = channel.host().context("Invalid S3 url")?.to_string(); let s3_config = config .as_ref() .and_then(|config| config.s3_options.0.get(&bucket)); - let region = region.or(s3_config.map(|c| c.region.clone())); - let endpoint_url = endpoint_url.or(s3_config.map(|c| c.endpoint_url.clone())); - let force_path_style = force_path_style.or(s3_config.map(|c| c.force_path_style)); + + // Fill in missing credentials from config file if not provided on command line + credentials.region = credentials.region.or(s3_config.map(|c| c.region.clone())); + credentials.endpoint_url = credentials + .endpoint_url + .or(s3_config.map(|c| c.endpoint_url.clone())); + + // Resolve the credentials + let credentials = match Option::::from(credentials) { + Some(credentials) => { + let auth_storage = AuthenticationStorage::from_env_and_defaults()?; + credentials.resolve(&channel, &auth_storage).ok_or_else(|| anyhow::anyhow!("Could not find S3 credentials in the authentication storage, and no credentials were provided via the command line."))? + } + None => rattler_s3::ResolvedS3Credentials::from_sdk().await?, + }; index_s3(IndexS3Config { channel, - region, - endpoint_url, + credentials, force_path_style, - access_key_id, - secret_access_key, - session_token, target_platform: cli.target_platform, repodata_patch: cli.repodata_patch, write_zst: cli.write_zst.unwrap_or(true), diff --git a/crates/rattler_s3/Cargo.toml b/crates/rattler_s3/Cargo.toml new file mode 100644 index 0000000000..9b99d0e2f6 --- /dev/null +++ b/crates/rattler_s3/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rattler_s3" +version = "0.1.0" +description = "A crate to streamline interaction with S3 storage for rattler" +categories.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true +readme.workspace = true + +[dependencies] +clap = { workspace = true, optional = true } +thiserror = { workspace = true } +url = { workspace = true } + +aws-config = { workspace = true, features = ["sso", "credentials-process"] } +aws-sdk-s3 = { workspace = true } +aws-credential-types = { workspace = true } + +rattler_networking = { workspace = true } diff --git a/crates/rattler_s3/src/clap.rs b/crates/rattler_s3/src/clap.rs new file mode 100644 index 0000000000..3112f64582 --- /dev/null +++ b/crates/rattler_s3/src/clap.rs @@ -0,0 +1,46 @@ +use crate::S3Credentials; +use clap::Parser; +use url::Url; + +/// Manually specified S3 credentials, when these are used no credentials are +/// read through the AWS SDK. +/// +/// See [`super::S3Credentials`] for details on how these credentials are used. +#[derive(Clone, Debug, PartialEq, Parser)] +pub struct S3CredentialsOpts { + /// The endpoint URL of the S3 backend + #[arg(long, env = "S3_ENDPOINT_URL", requires_all = ["region"], help_heading = "S3 Credentials")] + pub endpoint_url: Option, + + /// The region of the S3 backend + #[arg(long, env = "S3_REGION", requires_all = ["endpoint_url"], help_heading = "S3 Credentials")] + pub region: Option, + + /// The access key ID for the S3 bucket. + #[arg(long, env = "S3_ACCESS_KEY_ID", requires_all = ["secret_access_key", "endpoint_url", "region"], help_heading = "S3 Credentials")] + pub access_key_id: Option, + + /// The secret access key for the S3 bucket. + #[arg(long, env = "S3_SECRET_ACCESS_KEY", requires_all = ["access_key_id", "endpoint_url", "region"], help_heading = "S3 Credentials")] + pub secret_access_key: Option, + + /// The session token for the S3 bucket. + #[arg(long, env = "S3_SESSION_TOKEN", requires_all = ["access_key_id", "secret_access_key", "endpoint_url", "region"], help_heading = "S3 Credentials")] + pub session_token: Option, +} + +impl From for Option { + fn from(value: S3CredentialsOpts) -> Self { + if let (Some(endpoint_url), Some(region)) = (value.endpoint_url, value.region) { + Some(S3Credentials { + endpoint_url, + region, + access_key_id: value.access_key_id, + secret_access_key: value.secret_access_key, + session_token: value.session_token, + }) + } else { + None + } + } +} diff --git a/crates/rattler_s3/src/lib.rs b/crates/rattler_s3/src/lib.rs new file mode 100644 index 0000000000..ff9e1f73e3 --- /dev/null +++ b/crates/rattler_s3/src/lib.rs @@ -0,0 +1,152 @@ +#[cfg(feature = "clap")] +pub mod clap; + +use aws_config::{BehaviorVersion, Region}; +use aws_credential_types::provider::error::CredentialsError; +use aws_sdk_s3::config::{Credentials, ProvideCredentials}; +use rattler_networking::{Authentication, AuthenticationStorage}; +use url::Url; + +/// Rattler based crates always either use S3 credentials specified by the user +/// through CLI arguments combined with credentials coming from `rattler auth`, +/// or everything is loaded through the AWS SDK. +#[derive(Debug, Clone)] +pub struct S3Credentials { + /// The endpoint URL of the S3 backend + pub endpoint_url: Url, + + /// The region of the S3 backend + pub region: String, + + /// The access key ID for the S3 bucket. + pub access_key_id: Option, + + /// The secret access key for the S3 bucket. + pub secret_access_key: Option, + + /// The session token for the S3 bucket. + pub session_token: Option, +} + +/// The resolved S3 credentials. +#[derive(Debug, Clone)] +pub struct ResolvedS3Credentials { + /// The endpoint URL of the S3 backend + pub endpoint_url: Url, + + /// The region of the S3 backend + pub region: String, + + /// The access key ID for the S3 bucket. + pub access_key_id: String, + + /// The secret access key for the S3 bucket. + pub secret_access_key: String, + + /// The session token for the S3 bucket. + pub session_token: Option, +} + +#[derive(Debug, thiserror::Error)] +pub enum FromSDKError { + #[error("No credentials provider found in AWS SDK configuration")] + NoCredentialsProvider, + + #[error("Could not determine region from AWS SDK configuration")] + MissingRegion, + + #[error("Could not determine endpoint from AWS SDK configuration")] + MissingEndpoint, + + #[error("Failed to parse endpoint from AWS SDK configuration")] + InvalidEndpoint(#[source] url::ParseError), + + #[error(transparent)] + CredentialsError(CredentialsError), +} + +impl ResolvedS3Credentials { + pub async fn from_sdk() -> Result { + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + + let region = config + .region() + .map(Region::to_string) + .ok_or(FromSDKError::MissingRegion)?; + let endpoint_url = match config.endpoint_url() { + Some(endpoint) => endpoint.parse().map_err(FromSDKError::InvalidEndpoint)?, + None => return Err(FromSDKError::MissingEndpoint), + }; + + let Some(credentials_provider) = config.credentials_provider() else { + return Err(FromSDKError::NoCredentialsProvider); + }; + let credentials: Credentials = credentials_provider + .provide_credentials() + .await + .map_err(FromSDKError::CredentialsError)?; + let access_key_id = credentials.access_key_id().to_string(); + let secret_access_key = credentials.secret_access_key().to_string(); + let session_token = credentials.session_token().map(ToString::to_string); + + Ok(Self { + endpoint_url, + region, + access_key_id, + secret_access_key, + session_token, + }) + } +} + +impl S3Credentials { + /// Try to resolve the S3 credentials using the provided authentication + /// storage. + pub fn resolve( + self, + bucket_url: &Url, + auth_storage: &AuthenticationStorage, + ) -> Option { + let (access_key_id, secret_access_key, session_token) = + if let (Some(access_key_id), Some(secret_access_key)) = + (self.access_key_id, self.secret_access_key) + { + (access_key_id, secret_access_key, self.session_token) + } else if let Some((access_key_id, secret_access_key, session_token)) = + load_s3_credentials_from_auth_storage(auth_storage, bucket_url.clone()) + { + // Use the credentials from the authentication storage if they are available. + (access_key_id, secret_access_key, session_token) + } else { + return None; + }; + + Some(ResolvedS3Credentials { + endpoint_url: self.endpoint_url, + region: self.region, + access_key_id, + secret_access_key, + session_token, + }) + } +} + +fn load_s3_credentials_from_auth_storage( + auth_storage: &AuthenticationStorage, + channel: Url, +) -> Option<(String, String, Option)> { + let auth = auth_storage.get_by_url(channel).ok()?; + if let ( + _, + Some(Authentication::S3Credentials { + access_key_id, + secret_access_key, + session_token, + }), + ) = auth + { + Some((access_key_id, secret_access_key, session_token)) + } else { + None + } +} diff --git a/crates/rattler_upload/Cargo.toml b/crates/rattler_upload/Cargo.toml index dd9a47fd36..11c10ba2e4 100644 --- a/crates/rattler_upload/Cargo.toml +++ b/crates/rattler_upload/Cargo.toml @@ -11,7 +11,7 @@ license.workspace = true readme.workspace = true [features] -s3 = ["rattler_networking/s3", "aws-config", "aws-sdk-s3"] +s3 = ["rattler_networking/s3", "rattler_s3"] [dependencies] rattler_conda_types = { workspace = true, default-features = false } @@ -21,6 +21,7 @@ rattler_redaction = { workspace = true, default-features = false } rattler_package_streaming = { workspace = true, default-features = false } rattler_config = { workspace = true, default-features = false } rattler_solve = { workspace = true } +rattler_s3 = { workspace = true, optional = true, features = ["clap"] } miette = { workspace = true, features = ["fancy"] } clap = { workspace = true, features = ["env"] } fs-err = { workspace = true, features = ["tokio"] } @@ -49,8 +50,6 @@ tokio = { workspace = true, features = [ "rt-multi-thread", "process", ] } -aws-config = { workspace = true, optional = true } -aws-sdk-s3 = { workspace = true, optional = true } [target.'cfg(not(target_os = "windows"))'.dependencies] sha2 = { workspace = true, features = ["asm"] } diff --git a/crates/rattler_upload/src/lib.rs b/crates/rattler_upload/src/lib.rs index 23a088cd4a..7f5e8df60b 100644 --- a/crates/rattler_upload/src/lib.rs +++ b/crates/rattler_upload/src/lib.rs @@ -54,13 +54,9 @@ pub async fn upload_from_args(args: UploadOpts) -> miette::Result<()> { upload::upload_package_to_s3( &store, s3_opts.channel, - s3_opts.endpoint_url, - s3_opts.region, - s3_opts.force_path_style, - s3_opts.access_key_id, - s3_opts.secret_access_key, - s3_opts.session_token, + s3_opts.credentials.into(), &args.package_files, + s3_opts.force_path_style, s3_opts.force, ) .await diff --git a/crates/rattler_upload/src/upload/opt.rs b/crates/rattler_upload/src/upload/opt.rs index 213db94a1f..48b9719c92 100644 --- a/crates/rattler_upload/src/upload/opt.rs +++ b/crates/rattler_upload/src/upload/opt.rs @@ -429,30 +429,13 @@ pub struct S3Opts { #[arg(short, long, env = "S3_CHANNEL", value_parser = parse_s3_url)] pub channel: Url, - /// The endpoint URL of the S3 backend - #[arg(long, env = "S3_ENDPOINT_URL")] - pub endpoint_url: Option, - - /// The region of the S3 backend - #[arg(long, env = "S3_REGION")] - pub region: Option, + #[clap(flatten)] + pub credentials: rattler_s3::clap::S3CredentialsOpts, /// Whether to use path-style S3 URLs - #[arg(long, env = "S3_FORCE_PATH_STYLE", default_value = "false")] + #[arg(long, env = "S3_FORCE_PATH_STYLE")] pub force_path_style: Option, - /// The access key ID for the S3 bucket. - #[arg(long, env = "S3_ACCESS_KEY_ID", requires_all = ["secret_access_key"])] - pub access_key_id: Option, - - /// The secret access key for the S3 bucket. - #[arg(long, env = "S3_SECRET_ACCESS_KEY", requires_all = ["access_key_id"])] - pub secret_access_key: Option, - - /// The session token for the S3 bucket. - #[arg(long, env = "S3_SESSION_TOKEN", requires_all = ["access_key_id", "secret_access_key"])] - pub session_token: Option, - /// Replace files if it already exists. #[arg(long)] pub force: bool, diff --git a/crates/rattler_upload/src/upload/s3.rs b/crates/rattler_upload/src/upload/s3.rs index 8e2b6976d5..75575e2182 100644 --- a/crates/rattler_upload/src/upload/s3.rs +++ b/crates/rattler_upload/src/upload/s3.rs @@ -1,13 +1,9 @@ use std::path::PathBuf; -use aws_config::{meta::region::RegionProviderChain, BehaviorVersion}; -use aws_sdk_s3::config::{Credentials, ProvideCredentials}; -use miette::{Context, IntoDiagnostic}; -use opendal::{ - services::{S3Config, S3}, - Configurator, ErrorKind, Operator, -}; -use rattler_networking::{Authentication, AuthenticationStorage}; +use miette::IntoDiagnostic; +use opendal::{services::S3Config, Configurator, ErrorKind, Operator}; +use rattler_networking::AuthenticationStorage; +use rattler_s3::{ResolvedS3Credentials, S3Credentials}; use url::Url; use crate::upload::package::ExtractedPackage; @@ -17,13 +13,9 @@ use crate::upload::package::ExtractedPackage; pub async fn upload_package_to_s3( auth_storage: &AuthenticationStorage, channel: Url, - endpoint_url: Option, - region: Option, - force_path_style: Option, - access_key_id: Option, - secret_access_key: Option, - session_token: Option, + credentials: Option, package_files: &Vec, + force_path_style: Option, force: bool, ) -> miette::Result<()> { let bucket = channel @@ -34,55 +26,23 @@ pub async fn upload_package_to_s3( let mut s3_config = S3Config::default(); s3_config.root = Some(channel.path().to_string()); s3_config.bucket = bucket.to_string(); - - // Determine region and endpoint URL. - let endpoint = endpoint_url - .map(|url| url.to_string()) - .or_else(|| std::env::var("AWS_ENDPOINT_URL").ok()) - .unwrap_or_else(|| String::from("https://s3.amazonaws.com")); - - let mut region = region; - if region.is_none() { - // Try to use the AWS SDK to determine the region. - let region_provider = RegionProviderChain::default_provider(); - region = region_provider.region().await.map(|r| r.to_string()); - } - if region.is_none() { - // If no region is provided, we try to detect it from the endpoint URL. - region = S3::detect_region(&endpoint, &s3_config.bucket).await; - } - s3_config.region = region; - s3_config.endpoint = Some(endpoint); - - // How to access the S3 bucket. s3_config.enable_virtual_host_style = force_path_style.is_none_or(|x| !x); - // Use credentials from the CLI if they are provided. - if let (Some(access_key_id), Some(secret_access_key)) = (access_key_id, secret_access_key) { - s3_config.secret_access_key = Some(secret_access_key); - s3_config.access_key_id = Some(access_key_id); - s3_config.session_token = session_token; - } else if let Some((access_key_id, secret_access_key, session_token)) = - load_s3_credentials_from_auth_storage(auth_storage, channel.clone())? - { - // Use the credentials from the authentication storage if they are available. - s3_config.access_key_id = Some(access_key_id); - s3_config.secret_access_key = Some(secret_access_key); - s3_config.session_token = session_token; - } else { - let config = aws_config::load_defaults(BehaviorVersion::latest()).await; - let Some(credentials_provider) = config.credentials_provider() else { - return Err(miette::miette!("No AWS credentials provider found",)); - }; - let credentials: Credentials = credentials_provider - .provide_credentials() - .await - .into_diagnostic() - .context("failed to determine AWS credentials")?; - s3_config.access_key_id = Some(credentials.access_key_id().to_string()); - s3_config.secret_access_key = Some(credentials.secret_access_key().to_string()); - s3_config.session_token = credentials.session_token().map(ToString::to_string); - } + // Resolve the credentials to use. + let resolved_credentials = match credentials { + Some(credentials) => credentials + .resolve(&channel, auth_storage) + .ok_or_else(|| miette::miette!("Could not find S3 credentials in the authentication storage, and no credentials were provided via the command line."))?, + None => { + ResolvedS3Credentials::from_sdk().await.into_diagnostic()? + } + }; + + s3_config.endpoint = Some(resolved_credentials.endpoint_url.to_string()); + s3_config.region = Some(resolved_credentials.region); + s3_config.access_key_id = Some(resolved_credentials.access_key_id); + s3_config.secret_access_key = Some(resolved_credentials.secret_access_key); + s3_config.session_token = resolved_credentials.session_token; let builder = s3_config.into_builder(); let op = Operator::new(builder).into_diagnostic()?.finish(); @@ -121,23 +81,3 @@ pub async fn upload_package_to_s3( Ok(()) } - -fn load_s3_credentials_from_auth_storage( - auth_storage: &AuthenticationStorage, - channel: Url, -) -> miette::Result)>> { - let auth = auth_storage.get_by_url(channel).into_diagnostic()?; - if let ( - _, - Some(Authentication::S3Credentials { - access_key_id, - secret_access_key, - session_token, - }), - ) = auth - { - Ok(Some((access_key_id, secret_access_key, session_token))) - } else { - Ok(None) - } -} diff --git a/pixi.lock b/pixi.lock index 0d57432150..4d3937c2cc 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4637,7 +4637,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 + hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef globs: - ../../Cargo.toml - ../Cargo.toml @@ -4651,7 +4651,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 + hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef globs: - ../../Cargo.toml - ../Cargo.toml @@ -4665,7 +4665,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 + hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef globs: - ../../Cargo.toml - ../Cargo.toml @@ -4677,7 +4677,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: a9a8ec4c3782c266a369f4bffb15a8708a9253d1364d24423e95ff7fdf64bee5 + hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef globs: - ../../Cargo.toml - ../Cargo.toml diff --git a/scripts/e2e/s3-minio.nu b/scripts/e2e/s3-minio.nu index ebded42680..1e2cf8b08a 100644 --- a/scripts/e2e/s3-minio.nu +++ b/scripts/e2e/s3-minio.nu @@ -1,7 +1,7 @@ #!/usr/bin/env nu # Paths -let tmp = ($env.RUNNER_TMP? | default $env.TMP) +let tmp = ($env.RUNNER_TEMP? | default $env.TEMP? | default "/tmp") let bin_dir = $tmp let data_dir = $"($tmp)/minio-data" let log_file = $"($tmp)/minio.log" @@ -41,45 +41,39 @@ print $"== Configuring bucket ($bucket_name)..." ^mc policy set download $"minio/($bucket_name)" print "== Upload packages to Minio" -let args = [ - upload s3 - --channel $"s3://($bucket_name)" - --access-key-id $root_user - --secret-access-key $root_password - --region "us-east-1" - --endpoint-url "http://localhost:9000" - --force-path-style true - test-data/packages/empty-0.1.0-h4616a5c_0.conda -] +(^rattler + upload s3 + --channel $"s3://($bucket_name)" + --access-key-id $root_user + --secret-access-key $root_password + --region "us-east-1" + --endpoint-url "http://localhost:9000" + --force-path-style true + test-data/packages/empty-0.1.0-h4616a5c_0.conda +) -^rattler ...$args - -print "== Index the channel ===" -let args = [ - s3 - $"s3://($bucket_name)" - --access-key-id $root_user - --secret-access-key $root_password - --region "us-east-1" - --endpoint-url "http://localhost:9000" - --force-path-style true -] - -^rattler-index ...$args +print "== Index the channel" +(^rattler-index + s3 + $"s3://($bucket_name)" + --access-key-id $root_user + --secret-access-key $root_password + --region "us-east-1" + --endpoint-url "http://localhost:9000" + --force-path-style true +) print "== Test package can be installed from the channel ===" -let args = [ - create - --dry-run - -c $"s3://($bucket_name)" - empty==0.1.0 -] - with-env { AWS_ACCESS_KEY_ID: $root_user AWS_SECRET_ACCESS_KEY: $root_password AWS_REGION: "us-east-1" AWS_ENDPOINT_URL: "http://localhost:9000" } { - ^rattler ...$args + (^rattler + create + --dry-run + -c $"s3://($bucket_name)" + empty==0.1.0 + ) } \ No newline at end of file From 94aa024f5f77f7cafe764878fbba343a32d4d7d3 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:48:03 +0200 Subject: [PATCH 29/45] fix: pre-commit --- .github/workflows/e2e-s3-tests.yml | 2 +- crates/rattler-bin/pixi.toml | 2 +- crates/rattler_index/pixi.toml | 2 +- scripts/e2e/s3-minio.nu | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index ab428c401a..74ca8d97bb 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -83,4 +83,4 @@ jobs: with: environments: s3 - - run: pixi run e2e-s3-aws \ No newline at end of file + - run: pixi run e2e-s3-aws diff --git a/crates/rattler-bin/pixi.toml b/crates/rattler-bin/pixi.toml index fb498d9d6e..c4b5df0a4f 100644 --- a/crates/rattler-bin/pixi.toml +++ b/crates/rattler-bin/pixi.toml @@ -8,4 +8,4 @@ version = "0.3.*" channels = [ "https://prefix.dev/pixi-build-backends", "https://prefix.dev/conda-forge" -] \ No newline at end of file +] diff --git a/crates/rattler_index/pixi.toml b/crates/rattler_index/pixi.toml index 5f1fac93eb..bbc060d993 100644 --- a/crates/rattler_index/pixi.toml +++ b/crates/rattler_index/pixi.toml @@ -8,4 +8,4 @@ version = "0.3.*" channels = [ "https://prefix.dev/pixi-build-backends", "https://prefix.dev/conda-forge" -] \ No newline at end of file +] diff --git a/scripts/e2e/s3-minio.nu b/scripts/e2e/s3-minio.nu index 1e2cf8b08a..d9d8218efc 100644 --- a/scripts/e2e/s3-minio.nu +++ b/scripts/e2e/s3-minio.nu @@ -76,4 +76,4 @@ with-env { -c $"s3://($bucket_name)" empty==0.1.0 ) -} \ No newline at end of file +} From 43e687d7c9a4b38c7b71dcf973989f8a4a5410c1 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:00:27 +0200 Subject: [PATCH 30/45] cleaned up python bindings --- Cargo.lock | 1 + crates/rattler_index/src/lib.rs | 3 +- .../rattler_networking/src/s3_middleware.rs | 14 +++++- .../src/fetch/with_cache.rs | 7 ++- .../src/gateway/sharded_subdir/tokio/index.rs | 7 ++- crates/rattler_s3/Cargo.toml | 2 + crates/rattler_s3/src/lib.rs | 2 + py-rattler/Cargo.lock | 42 +++++++++++++++-- py-rattler/Cargo.toml | 2 + py-rattler/rattler/index/__init__.py | 4 +- py-rattler/rattler/index/index.py | 42 ++++++++++------- py-rattler/src/error.rs | 6 +++ py-rattler/src/index.rs | 45 +++++++++++-------- 13 files changed, 130 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ec72b441c..7817f7abb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4907,6 +4907,7 @@ dependencies = [ "aws-sdk-s3", "clap", "rattler_networking", + "serde", "thiserror 2.0.16", "url", ] diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index 082c6d4f99..a28048213f 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -41,6 +41,7 @@ const REPODATA_FROM_PACKAGES: &str = "repodata_from_packages.json"; const REPODATA: &str = "repodata.json"; const REPODATA_SHARDS: &str = "repodata_shards.msgpack.zst"; const ZSTD_REPODATA_COMPRESSION_LEVEL: i32 = 19; +const CACHE_CONTROL_IMMUTABLE: &str = "public, max-age=31536000, immutable"; /// Extract the package record from an `index.json` file. pub fn package_record_from_index_json( @@ -526,7 +527,7 @@ pub async fn write_repodata( match op .write_with(&shard_path, encoded_shard) .if_not_exists(true) - .cache_control("public, max-age=31536000, immutable") + .cache_control(CACHE_CONTROL_IMMUTABLE) .await { Err(e) if e.kind() == ErrorKind::ConditionNotMatch => { diff --git a/crates/rattler_networking/src/s3_middleware.rs b/crates/rattler_networking/src/s3_middleware.rs index e313368746..db97176f5f 100644 --- a/crates/rattler_networking/src/s3_middleware.rs +++ b/crates/rattler_networking/src/s3_middleware.rs @@ -58,6 +58,7 @@ pub struct S3 { config: HashMap, expiration: std::time::Duration, default_client: Arc>, + provider_name: &'static str, } /// S3 middleware to authenticate requests. @@ -84,8 +85,16 @@ impl S3 { auth_storage, expiration: std::time::Duration::from_secs(300), default_client: Arc::new(OnceCell::new()), + provider_name: "rattler", } } + + /// Sets the provider name that is used by the s3 client. + pub fn with_provider_name(mut self, name: &'static str) -> Self { + self.provider_name = name; + self + } + /// Create an S3 client. /// /// # Arguments @@ -130,7 +139,7 @@ impl S3 { secret_access_key, session_token, None, - "pixi", + self.provider_name, )), (_, Some(_)) => { return Err(anyhow::anyhow!("unsupported authentication method")); @@ -193,13 +202,14 @@ impl S3 { .presigned(presign_config) .await .context("failed to presign S3 PUT request")?, - _ => client + Method::GET => client .get_object() .bucket(bucket_name) .key(key) .presigned(presign_config) .await .context("failed to presign S3 GET request")?, + _ => unimplemented!("Only HEAD, POST and GET are supported for S3 requests"), }; Ok(Url::parse(presigned_request.uri()).context("failed to parse presigned S3 URL")?) diff --git a/crates/rattler_repodata_gateway/src/fetch/with_cache.rs b/crates/rattler_repodata_gateway/src/fetch/with_cache.rs index 275f228cf5..ab684159a2 100644 --- a/crates/rattler_repodata_gateway/src/fetch/with_cache.rs +++ b/crates/rattler_repodata_gateway/src/fetch/with_cache.rs @@ -434,11 +434,14 @@ pub async fn fetch_repo_data( // Fail if the status code is not a success if !response.status().is_success() { + let status = response.status(); + let body = response.text().await.ok(); return Err(FetchRepoDataError::HttpError( reqwest_middleware::Error::Middleware(anyhow::format_err!( - "received unexpected status code ({}) when fetching {}", - response.status(), + "received unexpected status code ({}) when fetching {}.\n\nBody:\n{}", + status, repo_data_url.redact(), + body.as_deref().unwrap_or(""), )), )); } diff --git a/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs b/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs index db8fc65e25..dbcd462977 100644 --- a/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs +++ b/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/tokio/index.rs @@ -50,10 +50,13 @@ pub async fn fetch_index( let mut url = response.url().clone().redact(); url.set_query(None); url.set_fragment(None); + let status = response.status(); + let body = response.text().await.ok(); return Err(GatewayError::ReqwestMiddlewareError(anyhow::format_err!( - "received unexpected status code ({}) when fetching {}", - response.status(), + "received unexpected status code ({}) when fetching {}.\n\nBody:\n{}", + status, url, + body.as_deref().unwrap_or("") ))); } diff --git a/crates/rattler_s3/Cargo.toml b/crates/rattler_s3/Cargo.toml index 9b99d0e2f6..485926412b 100644 --- a/crates/rattler_s3/Cargo.toml +++ b/crates/rattler_s3/Cargo.toml @@ -19,3 +19,5 @@ aws-sdk-s3 = { workspace = true } aws-credential-types = { workspace = true } rattler_networking = { workspace = true } + +serde = { workspace = true, optional = true } diff --git a/crates/rattler_s3/src/lib.rs b/crates/rattler_s3/src/lib.rs index ff9e1f73e3..3d57ebaeab 100644 --- a/crates/rattler_s3/src/lib.rs +++ b/crates/rattler_s3/src/lib.rs @@ -11,6 +11,7 @@ use url::Url; /// through CLI arguments combined with credentials coming from `rattler auth`, /// or everything is loaded through the AWS SDK. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct S3Credentials { /// The endpoint URL of the S3 backend pub endpoint_url: Url, @@ -30,6 +31,7 @@ pub struct S3Credentials { /// The resolved S3 credentials. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ResolvedS3Credentials { /// The endpoint URL of the S3 backend pub endpoint_url: Url, diff --git a/py-rattler/Cargo.lock b/py-rattler/Cargo.lock index 971c6db968..75f99e1297 100644 --- a/py-rattler/Cargo.lock +++ b/py-rattler/Cargo.lock @@ -259,6 +259,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.4.0" @@ -367,9 +373,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.4" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68c2194a190e1efc999612792e25b1ab3abfefe4306494efaaabc25933c0cbe" +checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -689,9 +695,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.8.3" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852b9226cb60b78ce9369022c0df678af1cac231c882d5da97a0c4e03be6e67" +checksum = "07f5e0fc8a6b3f2303f331b94504bbf754d85488f402d6f1dd7a6080f99afe56" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -3620,6 +3626,7 @@ dependencies = [ "pyo3-async-runtimes", "pyo3-build-config", "pyo3-file", + "pythonize", "rattler", "rattler_conda_types", "rattler_config", @@ -3629,6 +3636,7 @@ dependencies = [ "rattler_networking", "rattler_package_streaming", "rattler_repodata_gateway", + "rattler_s3", "rattler_shell", "rattler_solve", "rattler_virtual_packages", @@ -3727,6 +3735,16 @@ dependencies = [ "syn", ] +[[package]] +name = "pythonize" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597907139a488b22573158793aa7539df36ae863eba300c75f3a0d65fc475e27" +dependencies = [ + "pyo3", + "serde", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -4059,6 +4077,7 @@ dependencies = [ "rattler_digest", "rattler_networking", "rattler_package_streaming", + "rattler_s3", "reqwest", "rmp-serde", "serde", @@ -4137,6 +4156,7 @@ name = "rattler_networking" version = "0.25.10" dependencies = [ "anyhow", + "async-once-cell", "async-trait", "aws-config", "aws-sdk-s3", @@ -4264,6 +4284,20 @@ dependencies = [ "zstd", ] +[[package]] +name = "rattler_s3" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-credential-types", + "aws-sdk-s3", + "clap", + "rattler_networking", + "serde", + "thiserror 2.0.16", + "url", +] + [[package]] name = "rattler_shell" version = "0.24.9" diff --git a/py-rattler/Cargo.toml b/py-rattler/Cargo.toml index c8df3fecc9..4d8dd2bc1c 100644 --- a/py-rattler/Cargo.toml +++ b/py-rattler/Cargo.toml @@ -28,6 +28,7 @@ chrono = { version = "0.4" } futures = "0.3.31" parking_lot = { version = "0.12.3", features = ["arc_lock", "send_guard"] } +rattler_s3 = { path = "../crates/rattler_s3", features = ["serde"]} rattler = { path = "../crates/rattler", default-features = false, features = [ "indicatif", ] } @@ -58,6 +59,7 @@ pyo3 = { version = "0.25.1", features = [ "chrono", ] } pyo3-async-runtimes = { version = "0.25.0", features = ["tokio-runtime"] } +pythonize = "0.25.0" tokio = { version = "1.46.1" } reqwest = { version = "0.12.15", default-features = false } diff --git a/py-rattler/rattler/index/__init__.py b/py-rattler/rattler/index/__init__.py index 060834cbba..60fb824c6e 100644 --- a/py-rattler/rattler/index/__init__.py +++ b/py-rattler/rattler/index/__init__.py @@ -1,3 +1,3 @@ -from rattler.index.index import index_fs, index_s3 +from rattler.index.index import index_fs, index_s3, S3Credentials -__all__ = ["index_s3", "index_fs"] +__all__ = ["index_s3", "index_fs", "S3Credentials"] diff --git a/py-rattler/rattler/index/index.py b/py-rattler/rattler/index/index.py index d4b70155ed..71dbf241ad 100644 --- a/py-rattler/rattler/index/index.py +++ b/py-rattler/rattler/index/index.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dataclasses import dataclass import os from typing import Optional @@ -7,6 +8,26 @@ from rattler.rattler import py_index_fs, py_index_s3 +@dataclass +class S3Credentials: + """Credentials for accessing an S3 backend.""" + + # The endpoint URL of the S3 backend + endpoint_url: str + + # The region of the S3 backend + region: str + + # The access key ID for the S3 bucket. + access_key_id: Optional[str] = None + + # The secret access key for the S3 bucket. + secret_access_key: Optional[str] = None + + # The session token for the S3 bucket. + session_token: Optional[str] = None + + async def index_fs( channel_directory: os.PathLike[str], target_platform: Optional[Platform] = None, @@ -46,12 +67,8 @@ async def index_fs( async def index_s3( channel_url: str, - region: Optional[str] = None, - endpoint_url: Optional[str] = None, - force_path_style: bool = False, - access_key_id: Optional[str] = None, - secret_access_key: Optional[str] = None, - session_token: Optional[str] = None, + credentials: Optional[S3Credentials] = None, + force_path_style: Optional[bool] = False, target_platform: Optional[Platform] = None, repodata_patch: Optional[str] = None, write_zst: bool = True, @@ -69,12 +86,9 @@ async def index_s3( Arguments: channel_url: An S3 URL (e.g., s3://my-bucket/my-channel that containins the subdirectories of dependencies to index. - region: The region of the S3 bucket. - endpoint_url: The endpoint URL of the S3 bucket. force_path_style: Whether to use path-style addressing for S3. - access_key_id: The access key ID to use for authentication. - secret_access_key: The secret access key to use for authentication. - session_token: The session token to use for authentication. + credentials: The credentials to use for accessing the S3 bucket. If not provided, will use the default + credentials from the environment. target_platform: A `Platform` to index dependencies for. repodata_patch: The name of the conda package (expected to be in the `noarch` subdir) that should be used for repodata patching. write_zst: Whether to write repodata.json.zst. @@ -84,12 +98,8 @@ async def index_s3( """ await py_index_s3( channel_url, - region, - endpoint_url, + credentials, force_path_style, - access_key_id, - secret_access_key, - session_token, target_platform._inner if target_platform else target_platform, repodata_patch, write_zst, diff --git a/py-rattler/src/error.rs b/py-rattler/src/error.rs index f1b05d045b..69b1f60aa5 100644 --- a/py-rattler/src/error.rs +++ b/py-rattler/src/error.rs @@ -1,6 +1,7 @@ use std::{error::Error, io}; use pyo3::{create_exception, exceptions::PyException, PyErr}; +use pyo3::exceptions::PyValueError; use rattler::install::TransactionError; use rattler_conda_types::{ ConvertSubdirError, InvalidPackageNameError, ParseArchError, ParseChannelError, @@ -85,6 +86,8 @@ pub enum PyRattlerError { InvalidHeaderNameError(#[from] reqwest::header::InvalidHeaderName), #[error(transparent)] InvalidHeaderValueError(#[from] reqwest::header::InvalidHeaderValue), + #[error(transparent)] + FromSdkError(#[from] rattler_s3::FromSDKError), } fn pretty_print_error(mut err: &dyn Error) -> String { @@ -185,6 +188,9 @@ impl From for PyErr { PyRattlerError::InvalidHeaderValueError(err) => { InvalidHeaderValueError::new_err(pretty_print_error(&err)) } + PyRattlerError::FromSdkError(err) => { + PyValueError::new_err(pretty_print_error(&err)) + } } } } diff --git a/py-rattler/src/index.rs b/py-rattler/src/index.rs index 4eadd1d7e2..2f420e7012 100644 --- a/py-rattler/src/index.rs +++ b/py-rattler/src/index.rs @@ -5,9 +5,12 @@ use rattler_config::config::concurrency::default_max_concurrent_solves; use rattler_index::{index_fs, index_s3, IndexFsConfig, IndexS3Config}; use url::Url; -use std::path::PathBuf; - use crate::{error::PyRattlerError, platform::PyPlatform}; +use pyo3::exceptions::PyValueError; +use pythonize::depythonize; +use rattler_networking::AuthenticationStorage; +use rattler_s3::{ResolvedS3Credentials, S3Credentials}; +use std::path::PathBuf; #[pyfunction] #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] @@ -41,38 +44,44 @@ pub fn py_index_fs( #[pyfunction] #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] -#[pyo3(signature = (channel_url, region=None, endpoint_url=None, force_path_style=None, access_key_id=None,secret_access_key=None, session_token=None, target_platform=None, repodata_patch=None, write_zst=true, write_shards=true, force=false, max_parallel=None))] -pub fn py_index_s3( - py: Python<'_>, +#[pyo3(signature = (channel_url, credentials=None, force_path_style=None, target_platform=None, repodata_patch=None, write_zst=true, write_shards=true, force=false, max_parallel=None))] +pub fn py_index_s3<'py>( + py: Python<'py>, channel_url: String, - region: Option, - endpoint_url: Option, + credentials: Option>, force_path_style: Option, - access_key_id: Option, - secret_access_key: Option, - session_token: Option, target_platform: Option, repodata_patch: Option, write_zst: bool, write_shards: bool, force: bool, max_parallel: Option, -) -> PyResult> { +) -> PyResult> { let channel_url = Url::parse(&channel_url).map_err(PyRattlerError::from)?; - let endpoint_url = match endpoint_url { - Some(url) => Some(Url::parse(&url).map_err(PyRattlerError::from)?), + let credentials = match credentials { + Some(dict) => { + let credentials: S3Credentials = depythonize(&dict)?; + let auth_storage = + AuthenticationStorage::from_env_and_defaults().map_err(PyRattlerError::from)?; + Some((credentials, auth_storage)) + } None => None, }; let target_platform = target_platform.map(Platform::from); future_into_py(py, async move { + // Resolve the credentials + let credentials = + match credentials { + Some((credentials, auth_storage)) => credentials + .resolve(&channel_url, &auth_storage) + .ok_or_else(|| PyValueError::new_err("could not resolve s3 credentials"))?, + None => ResolvedS3Credentials::from_sdk().await.map_err(PyRattlerError::from)?, + }; + index_s3(IndexS3Config { channel: channel_url, - region, - endpoint_url, + credentials, force_path_style, - access_key_id, - secret_access_key, - session_token, target_platform, repodata_patch, write_zst, From fbbe4e5c61221fc62994da975245a2ef9f9888e5 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:01:41 +0200 Subject: [PATCH 31/45] fix: cleanup pixi lock --- pixi.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pixi.lock b/pixi.lock index 4d3937c2cc..fa035c9641 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4583,7 +4583,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4597,7 +4597,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4611,7 +4611,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4623,7 +4623,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: e92c53ae39856f7e730f3edf6241e61d5d7aeb45f5c3df24a0661fdb712e0cdf + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4637,7 +4637,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4651,7 +4651,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4665,7 +4665,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4677,7 +4677,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: be29998c0bbd666475f2495c2a2f23e6f07c27591f20ec02bd4e574728d85cef + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml From 2bbc46dfe83012142db47c7cf0da4299e992f4df Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:10:18 +0200 Subject: [PATCH 32/45] fix: update lockfile --- pixi.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pixi.lock b/pixi.lock index fa035c9641..aedbb51cb2 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4583,7 +4583,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4597,7 +4597,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4611,7 +4611,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4623,7 +4623,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4637,7 +4637,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 + hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f globs: - ../../Cargo.toml - ../Cargo.toml @@ -4651,7 +4651,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 + hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f globs: - ../../Cargo.toml - ../Cargo.toml @@ -4665,7 +4665,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 + hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f globs: - ../../Cargo.toml - ../Cargo.toml @@ -4677,7 +4677,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 + hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f globs: - ../../Cargo.toml - ../Cargo.toml From 8d32bff890c6d70d72a63f6ef49f7401ad025e3c Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:11:44 +0200 Subject: [PATCH 33/45] fix: update lockfile --- pixi.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pixi.lock b/pixi.lock index aedbb51cb2..fa035c9641 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4583,7 +4583,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4597,7 +4597,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4611,7 +4611,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4623,7 +4623,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: e15169de627a6b616dda211511b0386e9e758c68225115bae31d2071289b1334 + hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e globs: - ../../Cargo.toml - ../Cargo.toml @@ -4637,7 +4637,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4651,7 +4651,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4665,7 +4665,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml @@ -4677,7 +4677,7 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: aaa1ca74a97ca7f4380b56343e7621b5bc5d20845c0ecb4f02659e3a402b263f + hash: 9c3c3a3c8c6b7e728eb43ce6f8c71c2c82c4df32f17f0fdba11fe9fb3759b191 globs: - ../../Cargo.toml - ../Cargo.toml From c82ba82492a51e9672cc378cf42476e680485d12 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:08:25 +0200 Subject: [PATCH 34/45] Update .github/workflows/e2e-s3-tests.yml Co-authored-by: Pavel Zwerschke --- .github/workflows/e2e-s3-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index 74ca8d97bb..cdbf006c51 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -45,6 +45,7 @@ jobs: - run: pixi run e2e-s3-minio + # TODO: add cloudflare R2 integration tests here as well e2e-aws-s3-test: name: E2E Upload/Index/Download [AWS S3] runs-on: ubuntu-latest From 1c9f3d586d79cbc0f152a1a7756deec19189b543 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:32:39 +0200 Subject: [PATCH 35/45] fix: update cargo lockfile --- Cargo.lock | 422 ++++++++++++++++++++++++++++------------------------- Cargo.toml | 3 +- 2 files changed, 223 insertions(+), 202 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ea25ba93c..36d4d4d512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,22 +198,25 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.30" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977eb15ea9efd848bb8a4a1a2500347ed7f0bf794edf0dc3ddcf439f43d36b23" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ - "compression-codecs", - "compression-core", + "bzip2", + "flate2", "futures-core", + "memchr", "pin-project-lite", "tokio", + "zstd", + "zstd-safe", ] [[package]] name = "async-executor" -version = "1.13.3" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", @@ -278,6 +281,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.4.0" @@ -386,9 +395,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.6" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2" +checksum = "1541072f81945fa1251f8795ef6c92c4282d74d59f88498ae7d4bf00f0ebdad9" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -423,9 +432,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.104.0" +version = "1.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c488cd6abb0ec9811c401894191932e941c5f91dc226043edacd0afa1634bc" +checksum = "af040a86ae4378b7ed2f62c83b36be1848709bbbf5757ec850d0e08596a26be9" dependencies = [ "aws-credential-types", "aws-runtime", @@ -457,9 +466,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.83.0" +version = "1.81.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cd43af212d2a1c4dedff6f044d7e1961e5d9e7cfe773d70f31d9842413886" +checksum = "79ede098271e3471036c46957cba2ba30888f53bda2515bf04b560614a30a36e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -479,9 +488,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.84.0" +version = "1.82.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ec4a95bd48e0db7a424356a161f8d87bd6a4f0af37204775f0da03d9e39fc3" +checksum = "43326f724ba2cc957e6f3deac0ca1621a3e5d4146f5970c24c8a108dac33070f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -501,9 +510,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.85.0" +version = "1.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410309ad0df4606bc721aff0d89c3407682845453247213a0ccc5ff8801ee107" +checksum = "91abcdbfb48c38a0419eb75e0eac772a4783a96750392680e4f3c25a8a0535b9" dependencies = [ "aws-credential-types", "aws-runtime", @@ -563,9 +572,9 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.63.8" +version = "0.63.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d2df0314b8e307995a3b86d44565dfe9de41f876901a7d71886c756a25979f" +checksum = "4dbef71cd3cf607deb5c407df52f7e589e6849b296874ee448977efbb6d0832b" dependencies = [ "aws-smithy-http 0.62.3", "aws-smithy-types", @@ -635,9 +644,9 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147e8eea63a40315d704b97bf9bc9b8c1402ae94f89d5ad6f7550d963309da1b" +checksum = "4fdbad9bd9dbcc6c5e68c311a841b54b70def3ca3b674c42fbebb265980539f8" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -656,9 +665,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.5" +version = "0.61.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047" +checksum = "a16e040799d29c17412943bdbf488fd75db04112d0c0d4b9290bacf5ae0014b9" dependencies = [ "aws-smithy-types", ] @@ -684,9 +693,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.9.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3946acbe1ead1301ba6862e712c7903ca9bb230bdf1fbd1b5ac54158ef2ab1f" +checksum = "a3d57c8b53a72d15c8e190475743acf34e4996685e346a3448dd54ef696fc6e0" dependencies = [ "aws-smithy-async", "aws-smithy-http 0.62.3", @@ -891,11 +900,11 @@ checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bindgen" -version = "0.72.1" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "cexpr", "clang-sys", "itertools 0.13.0", @@ -915,7 +924,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "848df95320021558dd6bb4c26de3fe66724cdcbdbbf3fa720150b52b086ae568" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "libc", "log", "rustix 0.38.44", @@ -945,9 +954,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" [[package]] name = "bitvec" @@ -1017,7 +1026,7 @@ version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca5abbf2d4a4c6896197c9de13d6d7cb7eff438c63dacde1dde980569cb00248" dependencies = [ - "darling 0.21.3", + "darling 0.21.2", "ident_case", "prettyplease", "proc-macro2", @@ -1039,7 +1048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.9", "serde", ] @@ -1106,11 +1115,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.35" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ - "find-msvc-tools", "jobserver", "libc", "shlex", @@ -1202,9 +1210,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.46" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -1223,9 +1231,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -1285,26 +1293,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "compression-codecs" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" -dependencies = [ - "bzip2", - "compression-core", - "flate2", - "memchr", - "zstd", - "zstd-safe", -] - -[[package]] -name = "compression-core" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1326,7 +1314,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.10.0", ] [[package]] @@ -1585,12 +1573,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.21.3" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "08440b3dd222c3d0433e63e097463969485f112baff337dfdaca043a0d760570" dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", + "darling_core 0.21.2", + "darling_macro 0.21.2", ] [[package]] @@ -1609,9 +1597,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "d25b7912bc28a04ab1b7715a68ea03aaa15662b43a1a4b2c480531fd19f8bf7e" dependencies = [ "fnv", "ident_case", @@ -1634,11 +1622,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ce154b9bea7fb0c8e8326e62d00354000c36e79770ff21b8c84e3aa267d9d531" dependencies = [ - "darling_core 0.21.3", + "darling_core 0.21.2", "quote", "syn", ] @@ -1659,31 +1647,31 @@ dependencies = [ [[package]] name = "dbus" -version = "0.9.9" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" dependencies = [ "libc", "libdbus-sys", - "windows-sys 0.59.0", + "winapi", ] [[package]] name = "dbus-secret-service" -version = "4.1.0" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" dependencies = [ "aes", "block-padding", "cbc", "dbus", - "fastrand", + "futures-util", "hkdf", "num", "once_cell", + "rand 0.8.5", "sha2", - "zeroize", ] [[package]] @@ -1698,9 +1686,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -2031,12 +2019,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "find-msvc-tools" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" - [[package]] name = "fixedbitset" version = "0.5.7" @@ -2294,7 +2276,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] @@ -2319,8 +2301,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -2337,9 +2319,9 @@ dependencies = [ [[package]] name = "google-cloud-auth" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7314c5dcd0feb905728aa809f46d10a58587be2bdd90f3003e09bcef05e919dc" +checksum = "cb930bdeb70c93de0a8c635a23cfb2447e8da167ffe90eab1330b0a8b6091d2e" dependencies = [ "async-trait", "base64 0.22.1", @@ -2359,9 +2341,9 @@ dependencies = [ [[package]] name = "google-cloud-gax" -version = "0.24.0" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58148fad34ed71d986c8e3244c1575793445d211bee643740066e3a2388f4319" +checksum = "876a08b972e05565d562f2575e110092492835bf5f7b76aec4e90f792b581960" dependencies = [ "base64 0.22.1", "bytes", @@ -2379,9 +2361,9 @@ dependencies = [ [[package]] name = "google-cloud-rpc" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b443385571575a4552687a9072c4c8b7778e3f3c03fe95f3caf8df1a5e4ef2" +checksum = "07b375f1a978a69d4e4ebbf673766dc3be4bec01c8f95048896370205f2fe22c" dependencies = [ "bytes", "google-cloud-wkt", @@ -2429,7 +2411,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -2448,7 +2430,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.11.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -2943,9 +2925,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.5", @@ -3011,7 +2993,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -3079,9 +3061,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ "getrandom 0.3.3", "libc", @@ -3188,9 +3170,9 @@ checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libdbus-sys" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" dependencies = [ "pkg-config", ] @@ -3217,7 +3199,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "libc", "redox_syscall 0.5.17", ] @@ -3305,11 +3287,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "matchers" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -3477,7 +3459,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "libc", @@ -3490,7 +3472,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "libc", @@ -3535,11 +3517,12 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ - "windows-sys 0.52.0", + "overload", + "winapi", ] [[package]] @@ -3637,7 +3620,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", ] [[package]] @@ -3711,7 +3694,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "cfg-if", "foreign-types", "libc", @@ -3790,6 +3773,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "4.2.2" @@ -3874,7 +3863,7 @@ dependencies = [ "divan", "fs-err", "fxhash", - "indexmap 2.11.0", + "indexmap 2.10.0", "itertools 0.14.0", "proptest", "tempfile", @@ -3907,7 +3896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faee7227064121fcadcd2ff788ea26f0d8f2bd23a0574da11eca23bc935bcc05" dependencies = [ "boxcar", - "indexmap 2.11.0", + "indexmap 2.10.0", "itertools 0.13.0", "once_cell", "pep440_rs", @@ -3980,7 +3969,7 @@ checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.11.0", + "indexmap 2.10.0", "serde", ] @@ -4094,8 +4083,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", - "indexmap 2.11.0", - "quick-xml 0.38.3", + "indexmap 2.10.0", + "quick-xml 0.38.2", "serde", "time", ] @@ -4150,9 +4139,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ "zerovec", ] @@ -4208,13 +4197,13 @@ checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.4", + "bitflags 2.9.3", "lazy_static", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -4253,18 +4242,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.3" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8" dependencies = [ "memchr", ] [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", "cfg_aliases", @@ -4273,7 +4262,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.31", - "socket2 0.6.0", + "socket2 0.5.10", "thiserror 2.0.16", "tokio", "tracing", @@ -4282,9 +4271,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", "getrandom 0.3.3", @@ -4303,16 +4292,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4446,7 +4435,7 @@ dependencies = [ "fs-err", "futures", "humantime", - "indexmap 2.11.0", + "indexmap 2.10.0", "indicatif", "insta", "itertools 0.14.0", @@ -4496,6 +4485,7 @@ dependencies = [ "console 0.16.0", "indicatif", "itertools 0.14.0", + "miette", "once_cell", "rattler", "rattler_cache", @@ -4504,6 +4494,7 @@ dependencies = [ "rattler_networking", "rattler_repodata_gateway", "rattler_solve", + "rattler_upload", "rattler_virtual_packages", "reqwest", "reqwest-middleware", @@ -4566,7 +4557,7 @@ dependencies = [ "glob", "hex", "hex-literal", - "indexmap 2.11.0", + "indexmap 2.10.0", "insta", "itertools 0.14.0", "lazy-regex", @@ -4605,7 +4596,7 @@ version = "0.2.8" dependencies = [ "console 0.16.0", "fs-err", - "indexmap 2.11.0", + "indexmap 2.10.0", "insta", "rattler_conda_types", "serde", @@ -4656,6 +4647,7 @@ dependencies = [ "rattler_digest", "rattler_networking", "rattler_package_streaming", + "rattler_s3", "reqwest", "rmp-serde", "serde", @@ -4690,7 +4682,7 @@ dependencies = [ "chrono", "file_url", "fxhash", - "indexmap 2.11.0", + "indexmap 2.10.0", "insta", "itertools 0.14.0", "pep440_rs", @@ -4754,6 +4746,7 @@ name = "rattler_networking" version = "0.25.11" dependencies = [ "anyhow", + "async-once-cell", "async-trait", "aws-config", "aws-sdk-s3", @@ -4905,6 +4898,20 @@ dependencies = [ "zstd", ] +[[package]] +name = "rattler_s3" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-credential-types", + "aws-sdk-s3", + "clap", + "rattler_networking", + "serde", + "thiserror 2.0.16", + "url", +] + [[package]] name = "rattler_sandbox" version = "0.1.10" @@ -4923,7 +4930,7 @@ dependencies = [ "anyhow", "enum_dispatch", "fs-err", - "indexmap 2.11.0", + "indexmap 2.10.0", "insta", "itertools 0.14.0", "rattler_conda_types", @@ -4981,6 +4988,7 @@ dependencies = [ "rattler_networking", "rattler_package_streaming", "rattler_redaction", + "rattler_s3", "rattler_solve", "reqwest", "reqwest-middleware", @@ -5058,7 +5066,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", ] [[package]] @@ -5106,38 +5114,53 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] name = "regex-lite" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" @@ -5283,7 +5306,7 @@ dependencies = [ "event-listener", "futures", "human_bytes", - "indexmap 2.11.0", + "indexmap 2.10.0", "itertools 0.14.0", "petgraph", "serde", @@ -5390,12 +5413,13 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.21.3" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" dependencies = [ "cfg-if", "ordered-multimap", + "trim-in-place", ] [[package]] @@ -5425,7 +5449,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5438,7 +5462,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.9.4", @@ -5675,7 +5699,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5688,7 +5712,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5764,7 +5788,7 @@ version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.10.0", "itoa", "memchr", "ryu", @@ -5813,7 +5837,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.0", + "indexmap 2.10.0", "schemars 0.9.0", "schemars 1.0.4", "serde", @@ -5841,7 +5865,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -6149,7 +6173,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "byteorder", "enum-as-inner", "libc", @@ -6177,7 +6201,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.9.3", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6337,11 +6361,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.42" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", @@ -6351,15 +6376,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.23" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -6510,7 +6535,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime 0.7.0", @@ -6540,7 +6565,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.10.0", "toml_datetime 0.6.11", "winnow", ] @@ -6601,7 +6626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", - "bitflags 2.9.4", + "bitflags 2.9.3", "bytes", "futures-core", "futures-util", @@ -6680,14 +6705,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex-automata", + "regex", "sharded-slab", "smallvec", "thread_local", @@ -6717,6 +6742,12 @@ dependencies = [ "syn", ] +[[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + [[package]] name = "try-lock" version = "0.2.5" @@ -6881,9 +6912,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -6973,11 +7004,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.3+wasi-0.2.4" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ - "wit-bindgen", + "wit-bindgen-rt", ] [[package]] @@ -7081,9 +7112,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" dependencies = [ "futures", "js-sys", @@ -7510,9 +7541,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -7533,10 +7564,13 @@ dependencies = [ ] [[package]] -name = "wit-bindgen" -version = "0.45.0" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.3", +] [[package]] name = "writeable" @@ -7711,20 +7745,6 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "zerotrie" @@ -7768,7 +7788,7 @@ dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.11.0", + "indexmap 2.10.0", "memchr", "time", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index ca0c0f4b22..47be202f79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -190,14 +190,15 @@ rattler_lock = { path = "crates/rattler_lock", version = "=0.23.16", default-fea rattler_macros = { path = "crates/rattler_macros", version = "=1.0.11", default-features = false } rattler_menuinst = { path = "crates/rattler_menuinst", version = "=0.2.24", default-features = false } rattler_networking = { path = "crates/rattler_networking", version = "=0.25.11", default-features = false } +rattler_package_streaming = { path = "crates/rattler_package_streaming", version = "=0.23.2", default-features = false } rattler_pty = { path = "crates/rattler_pty", version = "=0.2.6", default-features = false } rattler_redaction = { path = "crates/rattler_redaction", version = "=0.1.12", default-features = false } -rattler_package_streaming = { path = "crates/rattler_package_streaming", version = "=0.23.2", default-features = false } rattler_repodata_gateway = { path = "crates/rattler_repodata_gateway", version = "=0.24.2", default-features = false } rattler_s3 = { path = "crates/rattler_s3", version = "=0.1.0", default-features = false } rattler_sandbox = { path = "crates/rattler_sandbox", version = "=0.1.10", default-features = false } rattler_shell = { path = "crates/rattler_shell", version = "=0.24.10", default-features = false } rattler_solve = { path = "crates/rattler_solve", version = "=3.0.2", default-features = false } +rattler_upload = { path = "crates/rattler_upload", version = "=0.2.1", default-features = false } rattler_virtual_packages = { path = "crates/rattler_virtual_packages", version = "=2.1.4", default-features = false } # This is also a rattler crate, but we only pin it to minor version From 65ecf077d3596e6c3c1aa8c4763a408b3406383a Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:21:00 +0200 Subject: [PATCH 36/45] fix: update lockfile --- crates/rattler-bin/pixi.toml | 2 +- crates/rattler_index/pixi.toml | 2 +- pixi.lock | 20 ++++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/rattler-bin/pixi.toml b/crates/rattler-bin/pixi.toml index c4b5df0a4f..4d2e1c932e 100644 --- a/crates/rattler-bin/pixi.toml +++ b/crates/rattler-bin/pixi.toml @@ -4,7 +4,7 @@ version = "0.1.0" [package.build.backend] name = "pixi-build-rust" -version = "0.3.*" +version = "0.4.*" channels = [ "https://prefix.dev/pixi-build-backends", "https://prefix.dev/conda-forge" diff --git a/crates/rattler_index/pixi.toml b/crates/rattler_index/pixi.toml index bbc060d993..93e7f5deca 100644 --- a/crates/rattler_index/pixi.toml +++ b/crates/rattler_index/pixi.toml @@ -4,7 +4,7 @@ version = "0.24.10" [package.build.backend] name = "pixi-build-rust" -version = "0.3.*" +version = "0.4.*" channels = [ "https://prefix.dev/pixi-build-backends", "https://prefix.dev/conda-forge" diff --git a/pixi.lock b/pixi.lock index 081075b9db..b3eb467c05 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5358,9 +5358,10 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 78ff9468b891954bbdc98d3bc40b309b178896b563fabcbb5e039a09f16d8c6e + hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -5371,9 +5372,10 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: 78ff9468b891954bbdc98d3bc40b309b178896b563fabcbb5e039a09f16d8c6e + hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -5384,9 +5386,10 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: 78ff9468b891954bbdc98d3bc40b309b178896b563fabcbb5e039a09f16d8c6e + hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index @@ -5395,9 +5398,10 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: 78ff9468b891954bbdc98d3bc40b309b178896b563fabcbb5e039a09f16d8c6e + hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da globs: - - ../../**/Cargo.toml + - ../../Cargo.toml + - ../Cargo.toml - Cargo.toml - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c From c83667197f031c3a9fd0bb900cfe898d2b68d6f9 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:23:52 +0200 Subject: [PATCH 37/45] fix: linting --- .github/workflows/e2e-s3-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-s3-tests.yml b/.github/workflows/e2e-s3-tests.yml index cdbf006c51..5ae62755cf 100644 --- a/.github/workflows/e2e-s3-tests.yml +++ b/.github/workflows/e2e-s3-tests.yml @@ -1,6 +1,6 @@ on: push: - branches: [ main ] + branches: [main] pull_request: paths: - crates/rattler-bin/** @@ -75,7 +75,7 @@ jobs: submodules: recursive - name: Configure AWS (OIDC) - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: aws-region: ${{ env.AWS_REGION }} role-to-assume: arn:aws:iam::239378270001:role/conda-rattler-e2e-test From 37ad55ac42fb33e2463275ec4b2cd347a421d178 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:26:32 +0200 Subject: [PATCH 38/45] feat: read version from cargo.toml --- crates/rattler-bin/pixi.toml | 1 - crates/rattler_index/pixi.toml | 1 - pixi.lock | 24 ++++++++++++------------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/rattler-bin/pixi.toml b/crates/rattler-bin/pixi.toml index 4d2e1c932e..aadfe368a1 100644 --- a/crates/rattler-bin/pixi.toml +++ b/crates/rattler-bin/pixi.toml @@ -1,6 +1,5 @@ [package] name = "rattler" -version = "0.1.0" [package.build.backend] name = "pixi-build-rust" diff --git a/crates/rattler_index/pixi.toml b/crates/rattler_index/pixi.toml index 93e7f5deca..578551923c 100644 --- a/crates/rattler_index/pixi.toml +++ b/crates/rattler_index/pixi.toml @@ -1,6 +1,5 @@ [package] name = "rattler_index" -version = "0.24.10" [package.build.backend] name = "pixi-build-rust" diff --git a/pixi.lock b/pixi.lock index b3eb467c05..ad953cf1b4 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5304,7 +5304,7 @@ packages: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: 74431efded7a33191f83e1b5a5029f244ccb9f6c000296f2770af01fdf2deabe globs: - ../../Cargo.toml - ../Cargo.toml @@ -5318,7 +5318,7 @@ packages: - __osx >=10.13 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: 74431efded7a33191f83e1b5a5029f244ccb9f6c000296f2770af01fdf2deabe globs: - ../../Cargo.toml - ../Cargo.toml @@ -5332,7 +5332,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: 74431efded7a33191f83e1b5a5029f244ccb9f6c000296f2770af01fdf2deabe globs: - ../../Cargo.toml - ../Cargo.toml @@ -5344,61 +5344,61 @@ packages: subdir: win-64 license: BSD-3-Clause input: - hash: 6e6f8d6f7effd2c3cc522fc0934c5fb9854a8d9d7abb8ad679280c2fba10269e + hash: 74431efded7a33191f83e1b5a5029f244ccb9f6c000296f2770af01fdf2deabe globs: - ../../Cargo.toml - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index - version: 0.24.10 + version: 0.24.12 build: ha7ad5ef_0 subdir: linux-64 constrains: - __glibc >=2.17 license: BSD-3-Clause input: - hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da + hash: aa940f198af68efa60f2ba08ee88ec50ac7a9e8120144d6102ce84bc550b369f globs: - ../../Cargo.toml - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index - version: 0.24.10 + version: 0.24.12 build: ha7ad5ef_0 subdir: osx-64 constrains: - __osx >=10.13 license: BSD-3-Clause input: - hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da + hash: aa940f198af68efa60f2ba08ee88ec50ac7a9e8120144d6102ce84bc550b369f globs: - ../../Cargo.toml - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index - version: 0.24.10 + version: 0.24.12 build: ha7ad5ef_0 subdir: osx-arm64 constrains: - __osx >=11.0 license: BSD-3-Clause input: - hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da + hash: aa940f198af68efa60f2ba08ee88ec50ac7a9e8120144d6102ce84bc550b369f globs: - ../../Cargo.toml - ../Cargo.toml - Cargo.toml - conda: crates/rattler_index name: rattler_index - version: 0.24.10 + version: 0.24.12 build: ha7ad5ef_0 subdir: win-64 license: BSD-3-Clause input: - hash: 0b737f5d81a34d1e50a015ccc2ec6495c69086cbe750871b55bd108f038908da + hash: aa940f198af68efa60f2ba08ee88ec50ac7a9e8120144d6102ce84bc550b369f globs: - ../../Cargo.toml - ../Cargo.toml From 866a11929919075231252c364963882ab110a1d9 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:32:39 +0200 Subject: [PATCH 39/45] fix: formatting issues --- py-rattler/src/error.rs | 6 ++---- py-rattler/src/index.rs | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/py-rattler/src/error.rs b/py-rattler/src/error.rs index 69b1f60aa5..1c209f9d8e 100644 --- a/py-rattler/src/error.rs +++ b/py-rattler/src/error.rs @@ -1,7 +1,7 @@ use std::{error::Error, io}; -use pyo3::{create_exception, exceptions::PyException, PyErr}; use pyo3::exceptions::PyValueError; +use pyo3::{create_exception, exceptions::PyException, PyErr}; use rattler::install::TransactionError; use rattler_conda_types::{ ConvertSubdirError, InvalidPackageNameError, ParseArchError, ParseChannelError, @@ -188,9 +188,7 @@ impl From for PyErr { PyRattlerError::InvalidHeaderValueError(err) => { InvalidHeaderValueError::new_err(pretty_print_error(&err)) } - PyRattlerError::FromSdkError(err) => { - PyValueError::new_err(pretty_print_error(&err)) - } + PyRattlerError::FromSdkError(err) => PyValueError::new_err(pretty_print_error(&err)), } } } diff --git a/py-rattler/src/index.rs b/py-rattler/src/index.rs index 2f420e7012..0efc682ea4 100644 --- a/py-rattler/src/index.rs +++ b/py-rattler/src/index.rs @@ -75,7 +75,9 @@ pub fn py_index_s3<'py>( Some((credentials, auth_storage)) => credentials .resolve(&channel_url, &auth_storage) .ok_or_else(|| PyValueError::new_err("could not resolve s3 credentials"))?, - None => ResolvedS3Credentials::from_sdk().await.map_err(PyRattlerError::from)?, + None => ResolvedS3Credentials::from_sdk() + .await + .map_err(PyRattlerError::from)?, }; index_s3(IndexS3Config { From 55feb95b3a9e52efccba42e59d259f502f99e118 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:01:46 +0200 Subject: [PATCH 40/45] fix: move force_path_style --- Cargo.lock | 1 + crates/rattler_index/src/lib.rs | 8 ++--- crates/rattler_index/src/main.rs | 6 ---- crates/rattler_s3/Cargo.toml | 5 +-- crates/rattler_s3/src/clap.rs | 48 +++++++++++++++++++++++-- crates/rattler_s3/src/lib.rs | 35 +++++++++++++++--- crates/rattler_upload/src/lib.rs | 1 - crates/rattler_upload/src/upload/opt.rs | 6 ++-- crates/rattler_upload/src/upload/s3.rs | 4 +-- py-rattler/Cargo.lock | 1 + py-rattler/rattler/index/index.py | 8 ++--- py-rattler/src/index.rs | 4 +-- scripts/e2e/s3-aws.nu | 9 ++--- 13 files changed, 96 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36d4d4d512..da9b75941d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4909,6 +4909,7 @@ dependencies = [ "rattler_networking", "serde", "thiserror 2.0.16", + "tracing", "url", ] diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index a28048213f..6e72eb9010 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -612,8 +612,6 @@ pub struct IndexS3Config { pub channel: Url, /// The resolved credentials to use for S3 access. pub credentials: ResolvedS3Credentials, - /// Whether to force path style for the S3 bucket. - pub force_path_style: Option, /// The target platform to index. pub target_platform: Option, /// The path to a repodata patch to apply to the index. @@ -636,7 +634,6 @@ pub async fn index_s3( IndexS3Config { channel, credentials, - force_path_style, target_platform, repodata_patch, write_zst, @@ -659,9 +656,8 @@ pub async fn index_s3( s3_config.secret_access_key = Some(credentials.secret_access_key); s3_config.access_key_id = Some(credentials.access_key_id); s3_config.session_token = credentials.session_token; - - // How to access the S3 bucket. - s3_config.enable_virtual_host_style = force_path_style.is_none_or(|x| !x); + s3_config.enable_virtual_host_style = + credentials.addressing_style == rattler_s3::S3AddressingStyle::VirtualHost; index( target_platform, diff --git a/crates/rattler_index/src/main.rs b/crates/rattler_index/src/main.rs index 33f8ae1a8f..8c17160b80 100644 --- a/crates/rattler_index/src/main.rs +++ b/crates/rattler_index/src/main.rs @@ -86,10 +86,6 @@ enum Commands { #[clap(flatten)] credentials: rattler_s3::clap::S3CredentialsOpts, - - /// Whether to use path-style S3 URLs - #[arg(long, env = "S3_FORCE_PATH_STYLE")] - force_path_style: Option, }, } @@ -136,7 +132,6 @@ async fn main() -> anyhow::Result<()> { Commands::S3 { channel, mut credentials, - force_path_style, } => { let bucket = channel.host().context("Invalid S3 url")?.to_string(); let s3_config = config @@ -161,7 +156,6 @@ async fn main() -> anyhow::Result<()> { index_s3(IndexS3Config { channel, credentials, - force_path_style, target_platform: cli.target_platform, repodata_patch: cli.repodata_patch, write_zst: cli.write_zst.unwrap_or(true), diff --git a/crates/rattler_s3/Cargo.toml b/crates/rattler_s3/Cargo.toml index 485926412b..177c7c0d60 100644 --- a/crates/rattler_s3/Cargo.toml +++ b/crates/rattler_s3/Cargo.toml @@ -12,10 +12,11 @@ readme.workspace = true [dependencies] clap = { workspace = true, optional = true } thiserror = { workspace = true } +tracing = { workspace = true } url = { workspace = true } -aws-config = { workspace = true, features = ["sso", "credentials-process"] } -aws-sdk-s3 = { workspace = true } +aws-config = { workspace = true, features = ["sso", "credentials-process", "rt-tokio", "rustls"] } +aws-sdk-s3 = { workspace = true, features = ["rustls", "rt-tokio"] } aws-credential-types = { workspace = true } rattler_networking = { workspace = true } diff --git a/crates/rattler_s3/src/clap.rs b/crates/rattler_s3/src/clap.rs index 3112f64582..3696aee6c4 100644 --- a/crates/rattler_s3/src/clap.rs +++ b/crates/rattler_s3/src/clap.rs @@ -1,7 +1,35 @@ -use crate::S3Credentials; -use clap::Parser; +use clap::{Parser, ValueEnum}; +use std::fmt::Display; use url::Url; +use crate::S3Credentials; + +#[derive(ValueEnum, Clone, Default, Debug, PartialEq)] +#[clap(rename_all = "kebab_case")] +pub enum S3AddressingStyleOpts { + #[default] + VirtualHost, + Path, +} + +impl From for crate::S3AddressingStyle { + fn from(value: S3AddressingStyleOpts) -> Self { + match value { + S3AddressingStyleOpts::VirtualHost => crate::S3AddressingStyle::VirtualHost, + S3AddressingStyleOpts::Path => crate::S3AddressingStyle::Path, + } + } +} + +impl Display for S3AddressingStyleOpts { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + S3AddressingStyleOpts::VirtualHost => write!(f, "virtual-host"), + S3AddressingStyleOpts::Path => write!(f, "path"), + } + } +} + /// Manually specified S3 credentials, when these are used no credentials are /// read through the AWS SDK. /// @@ -27,10 +55,23 @@ pub struct S3CredentialsOpts { /// The session token for the S3 bucket. #[arg(long, env = "S3_SESSION_TOKEN", requires_all = ["access_key_id", "secret_access_key", "endpoint_url", "region"], help_heading = "S3 Credentials")] pub session_token: Option, + + /// How to address the bucket + #[arg(long, env = "S3_ADDRESSING_STYLE", requires_all = ["region", "endpoint_url"], help_heading = "S3 Credentials", conflicts_with="force_path_style", default_value_t=S3AddressingStyleOpts::default())] + pub addressing_style: S3AddressingStyleOpts, + + /// Whether to use path-style S3 URLs + #[arg(long, env = "S3_FORCE_PATH_STYLE", requires_all = ["region", "endpoint_url"], help_heading = "S3 Credentials", conflicts_with="addressing_style", hide = true, help = "[deprecated] Whether to use path-style S3 URLs")] + pub force_path_style: Option, } impl From for Option { - fn from(value: S3CredentialsOpts) -> Self { + fn from(mut value: S3CredentialsOpts) -> Self { + if value.force_path_style.is_some() { + tracing::warn!("The `--force-path-style` option is deprecated, please use `--addressing-style=path` instead."); + value.addressing_style = S3AddressingStyleOpts::Path; + } + if let (Some(endpoint_url), Some(region)) = (value.endpoint_url, value.region) { Some(S3Credentials { endpoint_url, @@ -38,6 +79,7 @@ impl From for Option { access_key_id: value.access_key_id, secret_access_key: value.secret_access_key, session_token: value.session_token, + addressing_style: value.addressing_style.into(), }) } else { None diff --git a/crates/rattler_s3/src/lib.rs b/crates/rattler_s3/src/lib.rs index 3d57ebaeab..6cea40aa52 100644 --- a/crates/rattler_s3/src/lib.rs +++ b/crates/rattler_s3/src/lib.rs @@ -7,6 +7,19 @@ use aws_sdk_s3::config::{Credentials, ProvideCredentials}; use rattler_networking::{Authentication, AuthenticationStorage}; use url::Url; +/// How to address S3 buckets. +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] +pub enum S3AddressingStyle { + /// Address the bucket as a virtual host. E.g. . + #[default] + VirtualHost, + + /// Address the bucket using a path. E.g. . + Path, +} + /// Rattler based crates always either use S3 credentials specified by the user /// through CLI arguments combined with credentials coming from `rattler auth`, /// or everything is loaded through the AWS SDK. @@ -19,6 +32,10 @@ pub struct S3Credentials { /// The region of the S3 backend pub region: String, + /// The addressing style to use for the bucket. + #[cfg_attr(feature = "serde", serde(default))] + pub addressing_style: S3AddressingStyle, + /// The access key ID for the S3 bucket. pub access_key_id: Option, @@ -45,6 +62,9 @@ pub struct ResolvedS3Credentials { /// The secret access key for the S3 bucket. pub secret_access_key: String, + /// How to address the S3 buckets. + pub addressing_style: S3AddressingStyle, + /// The session token for the S3 bucket. pub session_token: Option, } @@ -70,15 +90,14 @@ pub enum FromSDKError { impl ResolvedS3Credentials { pub async fn from_sdk() -> Result { let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let s3_config = aws_sdk_s3::config::Builder::from(&config).build(); - let region = config + let region = s3_config .region() .map(Region::to_string) .ok_or(FromSDKError::MissingRegion)?; - let endpoint_url = match config.endpoint_url() { - Some(endpoint) => endpoint.parse().map_err(FromSDKError::InvalidEndpoint)?, - None => return Err(FromSDKError::MissingEndpoint), - }; + let endpoint_url_str = config.endpoint_url().unwrap_or("https://s3.amazonaws.com"); + let endpoint_url = Url::parse(endpoint_url_str).map_err(FromSDKError::InvalidEndpoint)?; let Some(credentials_provider) = config.credentials_provider() else { return Err(FromSDKError::NoCredentialsProvider); @@ -91,11 +110,16 @@ impl ResolvedS3Credentials { let secret_access_key = credentials.secret_access_key().to_string(); let session_token = credentials.session_token().map(ToString::to_string); + // Address style is not exposed in the AWS SDK config, so we use the default. + // See: + let addressing_style = S3AddressingStyle::default(); + Ok(Self { endpoint_url, region, access_key_id, secret_access_key, + addressing_style, session_token, }) } @@ -129,6 +153,7 @@ impl S3Credentials { access_key_id, secret_access_key, session_token, + addressing_style: self.addressing_style, }) } } diff --git a/crates/rattler_upload/src/lib.rs b/crates/rattler_upload/src/lib.rs index 7f5e8df60b..41adaf37fd 100644 --- a/crates/rattler_upload/src/lib.rs +++ b/crates/rattler_upload/src/lib.rs @@ -56,7 +56,6 @@ pub async fn upload_from_args(args: UploadOpts) -> miette::Result<()> { s3_opts.channel, s3_opts.credentials.into(), &args.package_files, - s3_opts.force_path_style, s3_opts.force, ) .await diff --git a/crates/rattler_upload/src/upload/opt.rs b/crates/rattler_upload/src/upload/opt.rs index 48b9719c92..e170a579b3 100644 --- a/crates/rattler_upload/src/upload/opt.rs +++ b/crates/rattler_upload/src/upload/opt.rs @@ -409,6 +409,7 @@ pub struct AnacondaOpts { pub force: bool, } +#[cfg(feature = "s3")] fn parse_s3_url(value: &str) -> Result { let url: Url = Url::parse(value).map_err(|err| format!("`{value}` isn't a valid URL: {err}"))?; @@ -422,6 +423,7 @@ fn parse_s3_url(value: &str) -> Result { } /// Options for uploading to S3 +#[cfg(feature = "s3")] #[derive(Clone, Debug, PartialEq, Parser)] pub struct S3Opts { /// The channel URL in the S3 bucket to upload the package to, e.g., @@ -432,10 +434,6 @@ pub struct S3Opts { #[clap(flatten)] pub credentials: rattler_s3::clap::S3CredentialsOpts, - /// Whether to use path-style S3 URLs - #[arg(long, env = "S3_FORCE_PATH_STYLE")] - pub force_path_style: Option, - /// Replace files if it already exists. #[arg(long)] pub force: bool, diff --git a/crates/rattler_upload/src/upload/s3.rs b/crates/rattler_upload/src/upload/s3.rs index 75575e2182..b1477fb2cc 100644 --- a/crates/rattler_upload/src/upload/s3.rs +++ b/crates/rattler_upload/src/upload/s3.rs @@ -15,7 +15,6 @@ pub async fn upload_package_to_s3( channel: Url, credentials: Option, package_files: &Vec, - force_path_style: Option, force: bool, ) -> miette::Result<()> { let bucket = channel @@ -26,7 +25,6 @@ pub async fn upload_package_to_s3( let mut s3_config = S3Config::default(); s3_config.root = Some(channel.path().to_string()); s3_config.bucket = bucket.to_string(); - s3_config.enable_virtual_host_style = force_path_style.is_none_or(|x| !x); // Resolve the credentials to use. let resolved_credentials = match credentials { @@ -43,6 +41,8 @@ pub async fn upload_package_to_s3( s3_config.access_key_id = Some(resolved_credentials.access_key_id); s3_config.secret_access_key = Some(resolved_credentials.secret_access_key); s3_config.session_token = resolved_credentials.session_token; + s3_config.enable_virtual_host_style = + resolved_credentials.addressing_style == rattler_s3::S3AddressingStyle::VirtualHost; let builder = s3_config.into_builder(); let op = Operator::new(builder).into_diagnostic()?.finish(); diff --git a/py-rattler/Cargo.lock b/py-rattler/Cargo.lock index a95f9d32fa..1e69b1d147 100644 --- a/py-rattler/Cargo.lock +++ b/py-rattler/Cargo.lock @@ -4295,6 +4295,7 @@ dependencies = [ "rattler_networking", "serde", "thiserror 2.0.16", + "tracing", "url", ] diff --git a/py-rattler/rattler/index/index.py b/py-rattler/rattler/index/index.py index 71dbf241ad..d93d3bb464 100644 --- a/py-rattler/rattler/index/index.py +++ b/py-rattler/rattler/index/index.py @@ -2,7 +2,7 @@ from dataclasses import dataclass import os -from typing import Optional +from typing import Optional, Literal from rattler.platform import Platform from rattler.rattler import py_index_fs, py_index_s3 @@ -27,6 +27,9 @@ class S3Credentials: # The session token for the S3 bucket. session_token: Optional[str] = None + # Defines how to address the bucket, either using virtual-hosted-style or path-style. + addressing_style: Literal["path", "virtual-host"] = "virtual-host" + async def index_fs( channel_directory: os.PathLike[str], @@ -68,7 +71,6 @@ async def index_fs( async def index_s3( channel_url: str, credentials: Optional[S3Credentials] = None, - force_path_style: Optional[bool] = False, target_platform: Optional[Platform] = None, repodata_patch: Optional[str] = None, write_zst: bool = True, @@ -86,7 +88,6 @@ async def index_s3( Arguments: channel_url: An S3 URL (e.g., s3://my-bucket/my-channel that containins the subdirectories of dependencies to index. - force_path_style: Whether to use path-style addressing for S3. credentials: The credentials to use for accessing the S3 bucket. If not provided, will use the default credentials from the environment. target_platform: A `Platform` to index dependencies for. @@ -99,7 +100,6 @@ async def index_s3( await py_index_s3( channel_url, credentials, - force_path_style, target_platform._inner if target_platform else target_platform, repodata_patch, write_zst, diff --git a/py-rattler/src/index.rs b/py-rattler/src/index.rs index 0efc682ea4..7b74a37707 100644 --- a/py-rattler/src/index.rs +++ b/py-rattler/src/index.rs @@ -44,12 +44,11 @@ pub fn py_index_fs( #[pyfunction] #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] -#[pyo3(signature = (channel_url, credentials=None, force_path_style=None, target_platform=None, repodata_patch=None, write_zst=true, write_shards=true, force=false, max_parallel=None))] +#[pyo3(signature = (channel_url, credentials=None, target_platform=None, repodata_patch=None, write_zst=true, write_shards=true, force=false, max_parallel=None))] pub fn py_index_s3<'py>( py: Python<'py>, channel_url: String, credentials: Option>, - force_path_style: Option, target_platform: Option, repodata_patch: Option, write_zst: bool, @@ -83,7 +82,6 @@ pub fn py_index_s3<'py>( index_s3(IndexS3Config { channel: channel_url, credentials, - force_path_style, target_platform, repodata_patch, write_zst, diff --git a/scripts/e2e/s3-aws.nu b/scripts/e2e/s3-aws.nu index 4b9248a58d..27f1709535 100644 --- a/scripts/e2e/s3-aws.nu +++ b/scripts/e2e/s3-aws.nu @@ -5,15 +5,14 @@ let region = ($env.AWS_REGION? | default "eu-west-1") def run [desc: string, cmd: closure] { print $"== ($desc)" - do $cmd | ignore - let code = ($env.LAST_EXIT_CODE? | default 0) + try { do $cmd } catch { } # run as-is + let code = ($env.LAST_EXIT_CODE? | default 0) # capture before any pipes if $code != 0 { print $"WARN: ($desc) failed \(exit=($code)\)" false } else { true } } - def bucket_exists [] { (do { ^aws s3api head-bucket --bucket $bucket_name } | complete).exit_code == 0 } @@ -41,4 +40,6 @@ if (bucket_exists) { } # --- exit non-zero if any step failed --- -if not $test_ok { exit 1 } +if not $test_ok { + exit 1 +} From 2857213faf8223c60ee4538b68f7b0e0b52a9b69 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:02:53 +0200 Subject: [PATCH 41/45] dont use deprecated api --- scripts/e2e/s3-minio.nu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/e2e/s3-minio.nu b/scripts/e2e/s3-minio.nu index d9d8218efc..045aaefee6 100644 --- a/scripts/e2e/s3-minio.nu +++ b/scripts/e2e/s3-minio.nu @@ -48,7 +48,7 @@ print "== Upload packages to Minio" --secret-access-key $root_password --region "us-east-1" --endpoint-url "http://localhost:9000" - --force-path-style true + --addressing-style path test-data/packages/empty-0.1.0-h4616a5c_0.conda ) @@ -60,7 +60,7 @@ print "== Index the channel" --secret-access-key $root_password --region "us-east-1" --endpoint-url "http://localhost:9000" - --force-path-style true + --addressing-style path ) print "== Test package can be installed from the channel ===" From 95a4d64d10055271a5947a2b528a82ada53f7fe9 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:07:48 +0200 Subject: [PATCH 42/45] chore: remove provider name --- crates/rattler_networking/src/s3_middleware.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/crates/rattler_networking/src/s3_middleware.rs b/crates/rattler_networking/src/s3_middleware.rs index db97176f5f..c8a1cf51e3 100644 --- a/crates/rattler_networking/src/s3_middleware.rs +++ b/crates/rattler_networking/src/s3_middleware.rs @@ -58,7 +58,6 @@ pub struct S3 { config: HashMap, expiration: std::time::Duration, default_client: Arc>, - provider_name: &'static str, } /// S3 middleware to authenticate requests. @@ -85,16 +84,9 @@ impl S3 { auth_storage, expiration: std::time::Duration::from_secs(300), default_client: Arc::new(OnceCell::new()), - provider_name: "rattler", } } - /// Sets the provider name that is used by the s3 client. - pub fn with_provider_name(mut self, name: &'static str) -> Self { - self.provider_name = name; - self - } - /// Create an S3 client. /// /// # Arguments @@ -139,7 +131,7 @@ impl S3 { secret_access_key, session_token, None, - self.provider_name, + "rattler", )), (_, Some(_)) => { return Err(anyhow::anyhow!("unsupported authentication method")); From 3ffe37ae1faa0e2bbbf463833558d1c9acea2fc3 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:35:39 +0200 Subject: [PATCH 43/45] Update crates/rattler_s3/src/lib.rs Co-authored-by: Pavel Zwerschke --- crates/rattler_s3/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/rattler_s3/src/lib.rs b/crates/rattler_s3/src/lib.rs index 6cea40aa52..a8f9e7fd3d 100644 --- a/crates/rattler_s3/src/lib.rs +++ b/crates/rattler_s3/src/lib.rs @@ -56,15 +56,15 @@ pub struct ResolvedS3Credentials { /// The region of the S3 backend pub region: String, + /// How to address the S3 buckets. + pub addressing_style: S3AddressingStyle, + /// The access key ID for the S3 bucket. pub access_key_id: String, /// The secret access key for the S3 bucket. pub secret_access_key: String, - /// How to address the S3 buckets. - pub addressing_style: S3AddressingStyle, - /// The session token for the S3 bucket. pub session_token: Option, } From c3983620c70454005894621e399a4d9b38b2437c Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:12:36 +0200 Subject: [PATCH 44/45] fix: integration test --- py-rattler/tests/unit/test_index.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/py-rattler/tests/unit/test_index.py b/py-rattler/tests/unit/test_index.py index 90769a1032..7b490f3e6f 100644 --- a/py-rattler/tests/unit/test_index.py +++ b/py-rattler/tests/unit/test_index.py @@ -11,6 +11,8 @@ from rattler import Platform from rattler.index import index_fs, index_s3 +from rattler.index.index import S3Credentials + # ------------------------------------ FILESYSTEM ------------------------------------ # @@ -130,11 +132,14 @@ async def test_index_s3( # Run index command await index_s3( channel_url=s3_channel, - region=s3_config.region, - endpoint_url=s3_config.endpoint_url, - force_path_style=True, - access_key_id=s3_config.access_key_id, - secret_access_key=s3_config.secret_access_key, + credentials=S3Credentials( + region=s3_config.region, + endpoint_url=s3_config.endpoint_url, + access_key_id=s3_config.access_key_id, + secret_access_key=s3_config.secret_access_key, + session_token=s3_config.session_token, + addressing_style="path", + ), repodata_patch=None, force=True, ) From 2b92d8ed8cebcbe57d9843515be7a22577f5427a Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:45:18 +0200 Subject: [PATCH 45/45] fix: struct ordering --- crates/rattler_s3/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rattler_s3/src/lib.rs b/crates/rattler_s3/src/lib.rs index a8f9e7fd3d..7fa4321a7c 100644 --- a/crates/rattler_s3/src/lib.rs +++ b/crates/rattler_s3/src/lib.rs @@ -117,9 +117,9 @@ impl ResolvedS3Credentials { Ok(Self { endpoint_url, region, + addressing_style, access_key_id, secret_access_key, - addressing_style, session_token, }) }