diff --git a/crates/pixi_config/src/lib.rs b/crates/pixi_config/src/lib.rs index 670d2e741..69f340c0e 100644 --- a/crates/pixi_config/src/lib.rs +++ b/crates/pixi_config/src/lib.rs @@ -280,6 +280,10 @@ pub struct PyPIConfig { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub keyring_provider: Option, + /// Allow insecure connections to a host + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub allow_insecure_host: Vec, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] @@ -412,6 +416,11 @@ impl PyPIConfig { index_url: other.index_url.or(self.index_url), extra_index_urls, keyring_provider: other.keyring_provider.or(self.keyring_provider), + allow_insecure_host: self + .allow_insecure_host + .into_iter() + .chain(other.allow_insecure_host) + .collect(), } } @@ -1360,6 +1369,22 @@ UNUSED = "unused" ); } + #[test] + fn test_pypi_config_allow_insecure_host() { + let toml = r#" + [pypi-config] + index-url = "https://pypi.org/simple" + extra-index-urls = ["https://pypi.org/simple2"] + keyring-provider = "subprocess" + allow-insecure-host = ["https://localhost:1234", "*"] + "#; + let (config, _) = Config::from_toml(toml).unwrap(); + assert_eq!( + config.pypi_config().allow_insecure_host, + vec!["https://localhost:1234", "*",] + ); + } + #[test] fn test_default_config() { let config = Config::default(); diff --git a/crates/pixi_config/src/snapshots/pixi_config__tests__config_merge.snap b/crates/pixi_config/src/snapshots/pixi_config__tests__config_merge.snap index 002e07945..e8d53846d 100644 --- a/crates/pixi_config/src/snapshots/pixi_config__tests__config_merge.snap +++ b/crates/pixi_config/src/snapshots/pixi_config__tests__config_merge.snap @@ -63,6 +63,7 @@ Config { index_url: None, extra_index_urls: [], keyring_provider: None, + allow_insecure_host: [], }, detached_environments: Some( Boolean( diff --git a/crates/pixi_uv_conversions/src/types.rs b/crates/pixi_uv_conversions/src/types.rs index 15e0859c9..26c6bd8e2 100644 --- a/crates/pixi_uv_conversions/src/types.rs +++ b/crates/pixi_uv_conversions/src/types.rs @@ -140,6 +140,9 @@ pub enum ConversionError { #[error(transparent)] InvalidVersion(#[from] VersionError), + + #[error(transparent)] + TrustedHostError(#[from] uv_configuration::TrustedHostError), } pub fn to_requirements<'req>( @@ -264,3 +267,10 @@ pub fn to_version_specifiers( .map_err(VersionSpecifiersError::PepVersionError)?, ) } + +/// Converts trusted_host `string` to `uv_configuration::TrustedHost` +pub fn to_uv_trusted_host( + trusted_host: &str, +) -> Result { + Ok(uv_configuration::TrustedHost::from_str(trusted_host)?) +} diff --git a/docs/reference/pixi_configuration.md b/docs/reference/pixi_configuration.md index e0ec237ec..55c0f1a56 100644 --- a/docs/reference/pixi_configuration.md +++ b/docs/reference/pixi_configuration.md @@ -185,6 +185,7 @@ To setup a certain number of defaults for the usage of PyPI registries. You can - `index-url`: The default index URL to use for PyPI packages. This will be added to a manifest file on a `pixi init`. - `extra-index-urls`: A list of additional URLs to use for PyPI packages. This will be added to a manifest file on a `pixi init`. - `keyring-provider`: Allows the use of the [keyring](https://pypi.org/project/keyring/) python package to store and retrieve credentials. +- `allow-insecure-host`: Allow insecure connections to host. ```toml title="config.toml" --8<-- "docs/source_files/pixi_config_tomls/main_config.toml:pypi-config" diff --git a/docs/source_files/pixi_config_tomls/main_config.toml b/docs/source_files/pixi_config_tomls/main_config.toml index ed30e677c..d1bac8af4 100644 --- a/docs/source_files/pixi_config_tomls/main_config.toml +++ b/docs/source_files/pixi_config_tomls/main_config.toml @@ -46,6 +46,8 @@ index-url = "https://pypi.org/simple" extra-index-urls = ["https://pypi.org/simple2"] # can be "subprocess" or "disabled" keyring-provider = "subprocess" +# allow insecure connections to host +allow-insecure-host = ["localhost:8080"] # --8<-- [end:pypi-config] # --8<-- [start:concurrency] diff --git a/src/install_pypi/mod.rs b/src/install_pypi/mod.rs index c8ff1b28f..21c7ac08f 100644 --- a/src/install_pypi/mod.rs +++ b/src/install_pypi/mod.rs @@ -79,6 +79,7 @@ pub async fn update_python_distributions( let registry_client = Arc::new( RegistryClientBuilder::new(uv_context.cache.clone()) .client(uv_context.client.clone()) + .allow_insecure_host(uv_context.allow_insecure_host.clone()) .index_urls(index_locations.index_urls()) .keyring(uv_context.keyring_provider) .connectivity(Connectivity::Online) diff --git a/src/lock_file/resolve/uv_resolution_context.rs b/src/lock_file/resolve/uv_resolution_context.rs index 593e741ff..0b0d8c59f 100644 --- a/src/lock_file/resolve/uv_resolution_context.rs +++ b/src/lock_file/resolve/uv_resolution_context.rs @@ -2,13 +2,14 @@ use std::sync::Arc; use miette::{Context, IntoDiagnostic}; use uv_cache::Cache; -use uv_configuration::{BuildOptions, Concurrency, SourceStrategy}; +use uv_configuration::{BuildOptions, Concurrency, SourceStrategy, TrustedHost}; use uv_distribution_types::IndexCapabilities; use uv_types::{HashStrategy, InFlight}; use crate::Project; use pixi_config::{self, get_cache_dir}; use pixi_consts::consts; +use pixi_uv_conversions::{to_uv_trusted_host, ConversionError}; /// Objects that are needed for resolutions which can be shared between different resolutions. #[derive(Clone)] @@ -22,6 +23,7 @@ pub struct UvResolutionContext { pub concurrency: Concurrency, pub source_strategy: SourceStrategy, pub capabilities: IndexCapabilities, + pub allow_insecure_host: Vec, } impl UvResolutionContext { @@ -47,6 +49,21 @@ impl UvResolutionContext { }; let in_flight = Arc::new(InFlight::default()); + let allow_insecure_host = project + .config() + .pypi_config + .allow_insecure_host + .iter() + .try_fold( + Vec::new(), + |mut hosts, host| -> Result, ConversionError> { + let parsed = to_uv_trusted_host(host)?; + hosts.push(parsed); + Ok(hosts) + }, + ) + .into_diagnostic() + .context("failed to parse trusted host")?; Ok(Self { cache, in_flight, @@ -57,6 +74,7 @@ impl UvResolutionContext { concurrency: Concurrency::default(), source_strategy: SourceStrategy::Disabled, capabilities: IndexCapabilities::default(), + allow_insecure_host, }) } }