Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 226 additions & 13 deletions crates/pixi_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,25 +120,37 @@ pub fn get_cache_dir() -> miette::Result<PathBuf> {
}
#[derive(Parser, Debug, Default, Clone)]
pub struct ConfigCli {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the liberty to order this alphabetically 😄

/// Do not verify the TLS certificate of the server.
#[arg(long, action = ArgAction::SetTrue, help_heading = consts::CLAP_CONFIG_OPTIONS)]
tls_no_verify: bool,

/// Path to the file containing the authentication token.
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS)]
auth_file: Option<PathBuf>,

/// Max concurrent network requests, default is `50`
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS)]
concurrent_downloads: Option<usize>,

/// Max concurrent solves, default is the number of CPUs
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS)]
concurrent_solves: Option<usize>,

/// Set pinning strategy
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS, value_enum)]
pinning_strategy: Option<PinningStrategy>,

/// Specifies whether to use the keyring to look up credentials for PyPI.
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS)]
pypi_keyring_provider: Option<KeyringProvider>,

/// Max concurrent solves, default is the number of CPUs
/// Run post-link scripts (insecure)
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS)]
pub concurrent_solves: Option<usize>,
run_post_link_scripts: bool,

/// Max concurrent network requests, default is `50`
/// Do not verify the TLS certificate of the server.
#[arg(long, action = ArgAction::SetTrue, help_heading = consts::CLAP_CONFIG_OPTIONS)]
tls_no_verify: bool,

/// Use environment activation cache (experimental)
#[arg(long, help_heading = consts::CLAP_CONFIG_OPTIONS)]
pub concurrent_downloads: Option<usize>,
use_environment_activation_cache: bool,
}

#[derive(Parser, Debug, Clone, Default)]
Expand Down Expand Up @@ -483,7 +495,7 @@ impl PyPIConfig {
}

/// The strategy for that will be used for pinning a version of a package.
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, Copy, clap::ValueEnum)]
#[serde(rename_all = "kebab-case")]
pub enum PinningStrategy {
/// Default semver strategy e.g. "1.2.3" becomes ">=1.2.3, <2" but "0.1.0"
Expand Down Expand Up @@ -767,6 +779,20 @@ impl From<ConfigCli> for Config {
.concurrent_downloads
.unwrap_or(ConcurrencyConfig::default().downloads),
},
tool_platform: None,
run_post_link_scripts: if cli.run_post_link_scripts {
Some(RunPostLinkScripts::Insecure)
} else {
None
},
experimental: ExperimentalConfig {
use_environment_activation_cache: if cli.use_environment_activation_cache {
Some(true)
} else {
None
},
},
pinning_strategy: cli.pinning_strategy,
..Default::default()
}
}
Expand Down Expand Up @@ -1262,10 +1288,13 @@ impl Config {
pub fn get_keys(&self) -> &[&str] {
&[
"authentication-override-file",
"concurrency",
"concurrency.downloads",
"concurrency.solves",
"default-channels",
"detached-environments",
"experimental",
"experimental.use-environment-activation-cache",
"max-concurrent-solves",
"mirrors",
"pinning-strategy",
"proxy-config",
Expand All @@ -1282,6 +1311,7 @@ impl Config {
"repodata-config.disable-jlap",
"repodata-config.disable-sharded",
"repodata-config.disable-zstd",
"run-post-link-scripts",
"s3-options",
"s3-options.<bucket>",
"s3-options.<bucket>.endpoint-url",
Expand Down Expand Up @@ -1934,26 +1964,44 @@ UNUSED = "unused"

#[test]
fn test_config_from_cli() {
// Test with all CLI options enabled
let cli = ConfigCli {
tls_no_verify: true,
auth_file: None,
pypi_keyring_provider: Some(KeyringProvider::Subprocess),
concurrent_solves: None,
concurrent_downloads: None,
concurrent_solves: Some(8),
concurrent_downloads: Some(100),
run_post_link_scripts: true,
use_environment_activation_cache: true,
pinning_strategy: Some(PinningStrategy::Semver),
};
let config = Config::from(cli);
assert_eq!(config.tls_no_verify, Some(true));
assert_eq!(
config.pypi_config().keyring_provider,
Some(KeyringProvider::Subprocess)
);
assert_eq!(config.concurrency.solves, 8);
assert_eq!(config.concurrency.downloads, 100);
assert_eq!(
config.run_post_link_scripts,
Some(RunPostLinkScripts::Insecure)
);
assert_eq!(
config.experimental.use_environment_activation_cache,
Some(true)
);
assert_eq!(config.pinning_strategy, Some(PinningStrategy::Semver));

let cli = ConfigCli {
tls_no_verify: false,
auth_file: Some(PathBuf::from("path.json")),
pypi_keyring_provider: None,
concurrent_solves: None,
concurrent_downloads: None,
run_post_link_scripts: false,
use_environment_activation_cache: false,
pinning_strategy: None,
};

let config = Config::from(cli);
Expand All @@ -1962,7 +2010,9 @@ UNUSED = "unused"
config.authentication_override_file,
Some(PathBuf::from("path.json"))
);
assert!(!config.experimental.use_environment_activation_cache());
assert_eq!(config.run_post_link_scripts, None);
assert_eq!(config.experimental.use_environment_activation_cache, None);
assert_eq!(config.pinning_strategy, None);
}

#[test]
Expand Down Expand Up @@ -2404,6 +2454,169 @@ UNUSED = "unused"
assert!(s3_options.force_path_style);
assert_eq!(s3_options.region, "auto");

// Test tool-platform
config
.set("tool-platform", Some("linux-64".to_string()))
.unwrap();
assert_eq!(config.tool_platform, Some(Platform::Linux64));

// Test run-post-link-scripts
config
.set("run-post-link-scripts", Some("insecure".to_string()))
.unwrap();
assert_eq!(
config.run_post_link_scripts,
Some(RunPostLinkScripts::Insecure)
);

// Test shell.force-activate
config
.set("shell.force-activate", Some("true".to_string()))
.unwrap();
assert_eq!(config.shell.force_activate, Some(true));

// Test shell.source-completion-scripts
config
.set("shell.source-completion-scripts", Some("false".to_string()))
.unwrap();
assert_eq!(config.shell.source_completion_scripts, Some(false));

// Test experimental.use-environment-activation-cache
config
.set(
"experimental.use-environment-activation-cache",
Some("true".to_string()),
)
.unwrap();
assert_eq!(
config.experimental.use_environment_activation_cache,
Some(true)
);

// Test more repodata-config options
config
.set("repodata-config.disable-bzip2", Some("true".to_string()))
.unwrap();
let repodata_config = config.repodata_config();
assert_eq!(repodata_config.default.disable_bzip2, Some(true));

config
.set("repodata-config.disable-zstd", Some("false".to_string()))
.unwrap();
let repodata_config = config.repodata_config();
assert_eq!(repodata_config.default.disable_zstd, Some(false));

config
.set("repodata-config.disable-sharded", Some("true".to_string()))
.unwrap();
let repodata_config = config.repodata_config();
assert_eq!(repodata_config.default.disable_sharded, Some(true));

// Test pypi-config.allow-insecure-host
config
.set(
"pypi-config.allow-insecure-host",
Some(r#"["pypi.example.com"]"#.to_string()),
)
.unwrap();
assert_eq!(config.pypi_config().allow_insecure_host.len(), 1);

// Test proxy-config
config
.set(
"proxy-config.http",
Some("http://proxy.example.com:8080".to_string()),
)
.unwrap();
assert_eq!(
config.proxy_config.http,
Some(Url::parse("http://proxy.example.com:8080").unwrap())
);

config
.set(
"proxy-config.https",
Some("https://proxy.example.com:8080".to_string()),
)
.unwrap();
assert_eq!(
config.proxy_config.https,
Some(Url::parse("https://proxy.example.com:8080").unwrap())
);

config
.set(
"proxy-config.non-proxy-hosts",
Some(r#"["localhost", "127.0.0.1"]"#.to_string()),
)
.unwrap();
assert_eq!(config.proxy_config.non_proxy_hosts.len(), 2);

// Test s3-options with individual keys
config
.set(
"s3-options.test-bucket.endpoint-url",
Some("http://localhost:9000".to_string()),
)
.unwrap();
config
.set(
"s3-options.test-bucket.region",
Some("us-east-1".to_string()),
)
.unwrap();
config
.set(
"s3-options.test-bucket.force-path-style",
Some("false".to_string()),
)
.unwrap();

// Test concurrency configuration
config
.set("concurrency.solves", Some("5".to_string()))
.unwrap();
assert_eq!(config.concurrency.solves, 5);

config
.set("concurrency.downloads", Some("25".to_string()))
.unwrap();
assert_eq!(config.concurrency.downloads, 25);

// Test max-concurrent-solves (legacy accessor)
assert_eq!(config.max_concurrent_solves(), 5);
assert_eq!(config.max_concurrent_downloads(), 25);

// Test tls-no-verify
config
.set("tls-no-verify", Some("true".to_string()))
.unwrap();
assert_eq!(config.tls_no_verify, Some(true));

// Test mirrors
config
.set(
"mirrors",
Some(r#"{"https://conda.anaconda.org/conda-forge": ["https://prefix.dev/conda-forge"]}"#.to_string()),
)
.unwrap();
assert_eq!(config.mirrors.len(), 1);

// Test detached-environments
config
.set("detached-environments", Some("/custom/path".to_string()))
.unwrap();
assert!(matches!(
config.detached_environments,
Some(DetachedEnvironments::Path(_))
));

// Test pinning-strategy
config
.set("pinning-strategy", Some("semver".to_string()))
.unwrap();
assert_eq!(config.pinning_strategy, Some(PinningStrategy::Semver));

config.set("unknown-key", None).unwrap_err();
}

Expand Down
19 changes: 13 additions & 6 deletions docs/reference/cli/pixi/add.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ pixi add [OPTIONS] <SPEC>...
: Whether the pypi requirement should be editable

## Config Options
- <a id="arg---tls-no-verify" href="#arg---tls-no-verify">`--tls-no-verify`</a>
: Do not verify the TLS certificate of the server
- <a id="arg---auth-file" href="#arg---auth-file">`--auth-file <AUTH_FILE>`</a>
: Path to the file containing the authentication token
- <a id="arg---concurrent-downloads" href="#arg---concurrent-downloads">`--concurrent-downloads <CONCURRENT_DOWNLOADS>`</a>
: Max concurrent network requests, default is `50`
- <a id="arg---concurrent-solves" href="#arg---concurrent-solves">`--concurrent-solves <CONCURRENT_SOLVES>`</a>
: Max concurrent solves, default is the number of CPUs
- <a id="arg---pinning-strategy" href="#arg---pinning-strategy">`--pinning-strategy <PINNING_STRATEGY>`</a>
: Set pinning strategy
<br>**options**: `semver`, `minor`, `major`, `latest-up`, `exact-version`, `no-pin`
- <a id="arg---pypi-keyring-provider" href="#arg---pypi-keyring-provider">`--pypi-keyring-provider <PYPI_KEYRING_PROVIDER>`</a>
: Specifies whether to use the keyring to look up credentials for PyPI
<br>**options**: `disabled`, `subprocess`
- <a id="arg---concurrent-solves" href="#arg---concurrent-solves">`--concurrent-solves <CONCURRENT_SOLVES>`</a>
: Max concurrent solves, default is the number of CPUs
- <a id="arg---concurrent-downloads" href="#arg---concurrent-downloads">`--concurrent-downloads <CONCURRENT_DOWNLOADS>`</a>
: Max concurrent network requests, default is `50`
- <a id="arg---run-post-link-scripts" href="#arg---run-post-link-scripts">`--run-post-link-scripts`</a>
: Run post-link scripts (insecure)
- <a id="arg---tls-no-verify" href="#arg---tls-no-verify">`--tls-no-verify`</a>
: Do not verify the TLS certificate of the server
- <a id="arg---use-environment-activation-cache" href="#arg---use-environment-activation-cache">`--use-environment-activation-cache`</a>
: Use environment activation cache (experimental)

## Git Options
- <a id="arg---git" href="#arg---git">`--git (-g) <GIT>`</a>
Expand Down
19 changes: 13 additions & 6 deletions docs/reference/cli/pixi/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,24 @@ pixi build [OPTIONS]
: Whether to clean the build directory before building

## Config Options
- <a id="arg---tls-no-verify" href="#arg---tls-no-verify">`--tls-no-verify`</a>
: Do not verify the TLS certificate of the server
- <a id="arg---auth-file" href="#arg---auth-file">`--auth-file <AUTH_FILE>`</a>
: Path to the file containing the authentication token
- <a id="arg---concurrent-downloads" href="#arg---concurrent-downloads">`--concurrent-downloads <CONCURRENT_DOWNLOADS>`</a>
: Max concurrent network requests, default is `50`
- <a id="arg---concurrent-solves" href="#arg---concurrent-solves">`--concurrent-solves <CONCURRENT_SOLVES>`</a>
: Max concurrent solves, default is the number of CPUs
- <a id="arg---pinning-strategy" href="#arg---pinning-strategy">`--pinning-strategy <PINNING_STRATEGY>`</a>
: Set pinning strategy
<br>**options**: `semver`, `minor`, `major`, `latest-up`, `exact-version`, `no-pin`
- <a id="arg---pypi-keyring-provider" href="#arg---pypi-keyring-provider">`--pypi-keyring-provider <PYPI_KEYRING_PROVIDER>`</a>
: Specifies whether to use the keyring to look up credentials for PyPI
<br>**options**: `disabled`, `subprocess`
- <a id="arg---concurrent-solves" href="#arg---concurrent-solves">`--concurrent-solves <CONCURRENT_SOLVES>`</a>
: Max concurrent solves, default is the number of CPUs
- <a id="arg---concurrent-downloads" href="#arg---concurrent-downloads">`--concurrent-downloads <CONCURRENT_DOWNLOADS>`</a>
: Max concurrent network requests, default is `50`
- <a id="arg---run-post-link-scripts" href="#arg---run-post-link-scripts">`--run-post-link-scripts`</a>
: Run post-link scripts (insecure)
- <a id="arg---tls-no-verify" href="#arg---tls-no-verify">`--tls-no-verify`</a>
: Do not verify the TLS certificate of the server
- <a id="arg---use-environment-activation-cache" href="#arg---use-environment-activation-cache">`--use-environment-activation-cache`</a>
: Use environment activation cache (experimental)

## Global Options
- <a id="arg---manifest-path" href="#arg---manifest-path">`--manifest-path <MANIFEST_PATH>`</a>
Expand Down
Loading
Loading