From 709e0ba238e7009916f64c2fcb6fab53d5ab714b Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 2 Sep 2025 08:37:12 -0500 Subject: [PATCH] Remove the native system store from the keyring providers (#15612) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're not sure what the best way to expose the native store to users is yet and it's a bit weird that you can use this in the `uv auth` commands but can't use any of the other keyring provider options. The simplest path forward is to just not expose it to users as a keyring provider, and instead frame it as a preview alternative to the plaintext uv credentials store. We can revisit the best way to expose configuration before stabilization. Note this pull request retains the _internal_ keyring provider implementation — we can refactor it out later but I wanted to avoid a bunch of churn here. --- .config/nextest.toml | 2 +- .github/workflows/ci.yml | 6 +- Cargo.lock | 3 +- crates/uv-auth/Cargo.toml | 1 + crates/uv-auth/src/middleware.rs | 35 ++- crates/uv-auth/src/store.rs | 34 +- crates/uv-client/src/base_client.rs | 6 +- crates/uv-configuration/Cargo.toml | 2 - crates/uv-configuration/src/authentication.rs | 16 +- crates/uv-keyring/Cargo.toml | 2 +- crates/uv-keyring/src/lib.rs | 4 +- crates/uv-keyring/src/macos.rs | 2 +- crates/uv-keyring/src/secret_service.rs | 2 +- crates/uv-keyring/src/windows.rs | 2 +- crates/uv-keyring/tests/basic.rs | 2 +- crates/uv-keyring/tests/threading.rs | 2 +- crates/uv-preview/src/lib.rs | 6 +- crates/uv/Cargo.toml | 2 +- crates/uv/src/commands/auth/login.rs | 8 +- crates/uv/src/commands/auth/logout.rs | 8 +- crates/uv/src/commands/auth/mod.rs | 39 --- crates/uv/src/commands/auth/token.rs | 8 +- crates/uv/src/commands/publish.rs | 7 +- crates/uv/src/lib.rs | 20 +- crates/uv/src/settings.rs | 48 +-- crates/uv/tests/it/auth.rs | 294 ++++-------------- crates/uv/tests/it/show_settings.rs | 4 +- docs/concepts/authentication/cli.md | 8 +- docs/concepts/authentication/http.md | 58 ++-- docs/concepts/preview.md | 2 + docs/reference/cli.md | 23 -- uv.schema.json | 5 - 32 files changed, 205 insertions(+), 456 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index a6c344d22..5692d4073 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -7,5 +7,5 @@ slow-timeout = { period = "10s", terminate-after = 12 } serial = { max-threads = 1 } [[profile.default.overrides]] -filter = 'test(native_keyring)' +filter = 'test(native_auth)' test-group = 'serial' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 168b96b06..19ed57211 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -254,7 +254,7 @@ jobs: UV_HTTP_RETRIES: 5 run: | cargo nextest run \ - --features python-patch,keyring-tests,secret-service \ + --features python-patch,native-auth,secret-service \ --workspace \ --status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow @@ -293,7 +293,7 @@ jobs: run: | cargo nextest run \ --no-default-features \ - --features python,python-managed,pypi,git,performance,crates-io,keyring-tests,apple-native \ + --features python,python-managed,pypi,git,performance,crates-io,native-auth,apple-native \ --workspace \ --status-level skip --failure-output immediate-final --no-fail-fast -j 12 --final-status-level slow @@ -345,7 +345,7 @@ jobs: run: | cargo nextest run \ --no-default-features \ - --features python,pypi,python-managed,keyring-tests,windows-native \ + --features python,pypi,python-managed,native-auth,windows-native \ --workspace \ --status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow diff --git a/Cargo.lock b/Cargo.lock index 5107ca912..991f8a208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5098,6 +5098,7 @@ dependencies = [ "uv-fs", "uv-keyring", "uv-once-map", + "uv-preview", "uv-redacted", "uv-small-str", "uv-state", @@ -5415,9 +5416,7 @@ dependencies = [ "uv-pep440", "uv-pep508", "uv-platform-tags", - "uv-preview", "uv-static", - "uv-warnings", ] [[package]] diff --git a/crates/uv-auth/Cargo.toml b/crates/uv-auth/Cargo.toml index 690b3cea0..ba194e801 100644 --- a/crates/uv-auth/Cargo.toml +++ b/crates/uv-auth/Cargo.toml @@ -13,6 +13,7 @@ workspace = true uv-fs = { workspace = true } uv-keyring = { workspace = true, features = ["apple-native", "secret-service", "windows-native"] } uv-once-map = { workspace = true } +uv-preview = { workspace = true } uv-redacted = { workspace = true } uv-small-str = { workspace = true } uv-static = { workspace = true } diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs index 443389e2a..841998397 100644 --- a/crates/uv-auth/src/middleware.rs +++ b/crates/uv-auth/src/middleware.rs @@ -7,6 +7,7 @@ use reqwest::{Request, Response}; use reqwest_middleware::{Error, Middleware, Next}; use tracing::{debug, trace, warn}; +use uv_preview::{Preview, PreviewFeatures}; use uv_redacted::DisplaySafeUrl; use crate::providers::HuggingFaceProvider; @@ -118,6 +119,7 @@ pub struct AuthMiddleware { /// Set all endpoints as needing authentication. We never try to send an /// unauthenticated request, avoiding cloning an uncloneable request. only_authenticated: bool, + preview: Preview, } impl AuthMiddleware { @@ -129,6 +131,7 @@ impl AuthMiddleware { cache: None, indexes: Indexes::new(), only_authenticated: false, + preview: Preview::default(), } } @@ -165,6 +168,13 @@ impl AuthMiddleware { self } + /// Configure the [`Preview`] features to use. + #[must_use] + pub fn with_preview(mut self, preview: Preview) -> Self { + self.preview = preview; + self + } + /// Configure the [`CredentialsCache`] to use. #[must_use] pub fn with_cache(mut self, cache: CredentialsCache) -> Self { @@ -598,9 +608,30 @@ impl AuthMiddleware { debug!("Checking text store for credentials for {url}"); text_store.get_credentials(url, credentials.as_ref().and_then(|credentials| credentials.username())).cloned() }) { - debug!("Found credentials in text store for {url}"); + debug!("Found credentials in plaintext store for {url}"); + Some(credentials) + } else if let Some(credentials) = { + if self.preview.is_enabled(PreviewFeatures::NATIVE_AUTH) { + let native_store = KeyringProvider::native(); + let username = credentials.and_then(|credentials| credentials.username()); + let display_username = if let Some(username) = username { + format!("{username}@") + } else { + String::new() + }; + if let Some(index_url) = maybe_index_url { + debug!("Checking native store for credentials for index URL {}{}", display_username, index_url); + native_store.fetch(DisplaySafeUrl::ref_cast(index_url), username).await + } else { + debug!("Checking native store for credentials for URL {}{}", display_username, url); + native_store.fetch(url, username).await + } + } else { + None + } + } { + debug!("Found credentials in native store for {url}"); Some(credentials) - // N.B. The keyring provider performs lookups for the exact URL then falls back to the host. // But, in the absence of an index URL, we cache the result per realm. So in that case, // if a keyring implementation returns different credentials for different URLs in the diff --git a/crates/uv-auth/src/store.rs b/crates/uv-auth/src/store.rs index 079bbaf21..43d89363a 100644 --- a/crates/uv-auth/src/store.rs +++ b/crates/uv-auth/src/store.rs @@ -7,15 +7,47 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use url::Url; use uv_fs::{LockedFile, with_added_extension}; +use uv_preview::{Preview, PreviewFeatures}; use uv_redacted::DisplaySafeUrl; use uv_state::{StateBucket, StateStore}; use uv_static::EnvVars; -use crate::Credentials; use crate::credentials::{Password, Username}; use crate::realm::Realm; use crate::service::Service; +use crate::{Credentials, KeyringProvider}; + +/// The storage backend to use in `uv auth` commands. +pub enum AuthBackend { + // TODO(zanieb): Right now, we're using a keyring provider for the system store but that's just + // where the native implementation is living at the moment. We should consider refactoring these + // into a shared API in the future. + System(KeyringProvider), + TextStore(TextCredentialStore, LockedFile), +} + +impl AuthBackend { + pub fn from_settings(preview: Preview) -> Result { + // If preview is enabled, we'll use the system-native store + if preview.is_enabled(PreviewFeatures::NATIVE_AUTH) { + return Ok(Self::System(KeyringProvider::native())); + } + + // Otherwise, we'll use the plaintext credential store + let path = TextCredentialStore::default_file()?; + match TextCredentialStore::read(&path) { + Ok((store, lock)) => Ok(Self::TextStore(store, lock)), + Err(TomlCredentialError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => { + Ok(Self::TextStore( + TextCredentialStore::default(), + TextCredentialStore::lock(&path)?, + )) + } + Err(err) => Err(err), + } + } +} /// Authentication scheme to use. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/uv-client/src/base_client.rs b/crates/uv-client/src/base_client.rs index 9292b2743..d4efe49cb 100644 --- a/crates/uv-client/src/base_client.rs +++ b/crates/uv-client/src/base_client.rs @@ -489,13 +489,15 @@ impl<'a> BaseClientBuilder<'a> { AuthIntegration::Default => { let auth_middleware = AuthMiddleware::new() .with_indexes(self.indexes.clone()) - .with_keyring(self.keyring.to_provider(&self.preview)); + .with_keyring(self.keyring.to_provider()) + .with_preview(self.preview); client = client.with(auth_middleware); } AuthIntegration::OnlyAuthenticated => { let auth_middleware = AuthMiddleware::new() .with_indexes(self.indexes.clone()) - .with_keyring(self.keyring.to_provider(&self.preview)) + .with_keyring(self.keyring.to_provider()) + .with_preview(self.preview) .with_only_authenticated(true); client = client.with(auth_middleware); diff --git a/crates/uv-configuration/Cargo.toml b/crates/uv-configuration/Cargo.toml index 7560dc926..655a115a9 100644 --- a/crates/uv-configuration/Cargo.toml +++ b/crates/uv-configuration/Cargo.toml @@ -25,9 +25,7 @@ uv-normalize = { workspace = true } uv-pep440 = { workspace = true } uv-pep508 = { workspace = true, features = ["schemars"] } uv-platform-tags = { workspace = true } -uv-preview = { workspace = true } uv-static = { workspace = true } -uv-warnings = { workspace = true } clap = { workspace = true, features = ["derive"], optional = true } either = { workspace = true } fs-err = { workspace = true } diff --git a/crates/uv-configuration/src/authentication.rs b/crates/uv-configuration/src/authentication.rs index 037266b4f..1ad7b5fbb 100644 --- a/crates/uv-configuration/src/authentication.rs +++ b/crates/uv-configuration/src/authentication.rs @@ -1,6 +1,4 @@ use uv_auth::{self, KeyringProvider}; -use uv_preview::{Preview, PreviewFeatures}; -use uv_warnings::warn_user_once; /// Keyring provider type to use for credential lookup. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -11,8 +9,6 @@ pub enum KeyringProviderType { /// Do not use keyring for credential lookup. #[default] Disabled, - /// Use a native integration with the system keychain for credential lookup. - Native, /// Use the `keyring` command for credential lookup. Subprocess, // /// Not yet implemented @@ -23,18 +19,9 @@ pub enum KeyringProviderType { // See for details. impl KeyringProviderType { - pub fn to_provider(&self, preview: &Preview) -> Option { + pub fn to_provider(&self) -> Option { match self { Self::Disabled => None, - Self::Native => { - if !preview.is_enabled(PreviewFeatures::NATIVE_KEYRING) { - warn_user_once!( - "The native keyring provider is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.", - PreviewFeatures::NATIVE_KEYRING - ); - } - Some(KeyringProvider::native()) - } Self::Subprocess => Some(KeyringProvider::subprocess()), } } @@ -44,7 +31,6 @@ impl std::fmt::Display for KeyringProviderType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Disabled => write!(f, "disabled"), - Self::Native => write!(f, "native"), Self::Subprocess => write!(f, "subprocess"), } } diff --git a/crates/uv-keyring/Cargo.toml b/crates/uv-keyring/Cargo.toml index 48dadc152..624ddd687 100644 --- a/crates/uv-keyring/Cargo.toml +++ b/crates/uv-keyring/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [features] default = ["apple-native", "secret-service", "windows-native"] -keyring-tests = [] +native-auth = [] ## Use the built-in Keychain Services on macOS apple-native = ["dep:security-framework"] diff --git a/crates/uv-keyring/src/lib.rs b/crates/uv-keyring/src/lib.rs index 9502c91dd..07f6a92fc 100644 --- a/crates/uv-keyring/src/lib.rs +++ b/crates/uv-keyring/src/lib.rs @@ -407,12 +407,12 @@ doc_comment::doctest!("../README.md", readme); /// passing their store-specific parameters for the generic ones. mod tests { use super::{Entry, Error}; - #[cfg(feature = "keyring-tests")] + #[cfg(feature = "native-auth")] use super::{Result, credential::CredentialApi}; use std::collections::HashMap; /// Create a platform-specific credential given the constructor, service, and user - #[cfg(feature = "keyring-tests")] + #[cfg(feature = "native-auth")] pub(crate) fn entry_from_constructor(f: F, service: &str, user: &str) -> Entry where F: FnOnce(Option<&str>, &str, &str) -> Result, diff --git a/crates/uv-keyring/src/macos.rs b/crates/uv-keyring/src/macos.rs index cbaeb463a..3595f82fe 100644 --- a/crates/uv-keyring/src/macos.rs +++ b/crates/uv-keyring/src/macos.rs @@ -331,7 +331,7 @@ pub fn decode_error(err: Error) -> ErrorCode { } } -#[cfg(feature = "keyring-tests")] +#[cfg(feature = "native-auth")] #[cfg(not(miri))] #[cfg(test)] mod tests { diff --git a/crates/uv-keyring/src/secret_service.rs b/crates/uv-keyring/src/secret_service.rs index 376e466aa..7ec4535d4 100644 --- a/crates/uv-keyring/src/secret_service.rs +++ b/crates/uv-keyring/src/secret_service.rs @@ -634,7 +634,7 @@ fn wrap(err: Error) -> Box { Box::new(err) } -#[cfg(feature = "keyring-tests")] +#[cfg(feature = "native-auth")] #[cfg(test)] mod tests { use crate::credential::CredentialPersistence; diff --git a/crates/uv-keyring/src/windows.rs b/crates/uv-keyring/src/windows.rs index 8ef57d46f..eb644f650 100644 --- a/crates/uv-keyring/src/windows.rs +++ b/crates/uv-keyring/src/windows.rs @@ -513,7 +513,7 @@ impl std::error::Error for Error { } } -#[cfg(feature = "keyring-tests")] +#[cfg(feature = "native-auth")] #[cfg(test)] mod tests { use super::*; diff --git a/crates/uv-keyring/tests/basic.rs b/crates/uv-keyring/tests/basic.rs index fc833f8ab..4bdbf26aa 100644 --- a/crates/uv-keyring/tests/basic.rs +++ b/crates/uv-keyring/tests/basic.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "keyring-tests")] +#![cfg(feature = "native-auth")] use common::{generate_random_bytes_of_len, generate_random_string, init_logger}; use uv_keyring::{Entry, Error}; diff --git a/crates/uv-keyring/tests/threading.rs b/crates/uv-keyring/tests/threading.rs index e2389755d..a67440934 100644 --- a/crates/uv-keyring/tests/threading.rs +++ b/crates/uv-keyring/tests/threading.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "keyring-tests")] +#![cfg(feature = "native-auth")] use common::{generate_random_string, init_logger}; use uv_keyring::{Entry, Error}; diff --git a/crates/uv-preview/src/lib.rs b/crates/uv-preview/src/lib.rs index ad30a4c71..4c122edde 100644 --- a/crates/uv-preview/src/lib.rs +++ b/crates/uv-preview/src/lib.rs @@ -18,7 +18,7 @@ bitflags::bitflags! { const EXTRA_BUILD_DEPENDENCIES = 1 << 6; const DETECT_MODULE_CONFLICTS = 1 << 7; const FORMAT = 1 << 8; - const NATIVE_KEYRING = 1 << 9; + const NATIVE_AUTH = 1 << 9; } } @@ -37,7 +37,7 @@ impl PreviewFeatures { Self::EXTRA_BUILD_DEPENDENCIES => "extra-build-dependencies", Self::DETECT_MODULE_CONFLICTS => "detect-module-conflicts", Self::FORMAT => "format", - Self::NATIVE_KEYRING => "native-keyring", + Self::NATIVE_AUTH => "native-auth", _ => panic!("`flag_as_str` can only be used for exactly one feature flag"), } } @@ -84,7 +84,7 @@ impl FromStr for PreviewFeatures { "extra-build-dependencies" => Self::EXTRA_BUILD_DEPENDENCIES, "detect-module-conflicts" => Self::DETECT_MODULE_CONFLICTS, "format" => Self::FORMAT, - "native-keyring" => Self::NATIVE_KEYRING, + "native-auth" => Self::NATIVE_AUTH, _ => { warn_user_once!("Unknown preview feature: `{part}`"); continue; diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index e38a07ef7..db55b1892 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -152,7 +152,7 @@ ignored = [ [features] default = ["performance", "uv-distribution/static", "default-tests"] -keyring-tests = [] +native-auth = [] # Use better memory allocators, etc. performance = ["performance-memory-allocator"] performance-memory-allocator = ["dep:uv-performance-memory-allocator"] diff --git a/crates/uv/src/commands/auth/login.rs b/crates/uv/src/commands/auth/login.rs index 0c740e5d6..ebe85796b 100644 --- a/crates/uv/src/commands/auth/login.rs +++ b/crates/uv/src/commands/auth/login.rs @@ -5,13 +5,12 @@ use console::Term; use owo_colors::OwoColorize; use uv_auth::Service; +use uv_auth::store::AuthBackend; use uv_auth::{Credentials, TextCredentialStore}; -use uv_configuration::KeyringProviderType; use uv_distribution_types::IndexUrl; use uv_pep508::VerbatimUrl; use uv_preview::Preview; -use crate::commands::auth::AuthBackend; use crate::{commands::ExitStatus, printer::Printer}; /// Login to a service. @@ -20,11 +19,10 @@ pub(crate) async fn login( username: Option, password: Option, token: Option, - keyring_provider: Option, printer: Printer, preview: Preview, ) -> Result { - let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?; + let backend = AuthBackend::from_settings(preview)?; // If the URL includes a known index URL suffix, strip it // TODO(zanieb): Use a shared abstraction across `login` and `logout`? @@ -112,7 +110,7 @@ pub(crate) async fn login( // TODO(zanieb): Add support for other authentication schemes here, e.g., `Credentials::Bearer` let credentials = Credentials::basic(Some(username), Some(password)); match backend { - AuthBackend::Keyring(provider) => { + AuthBackend::System(provider) => { provider.store(&url, &credentials).await?; } AuthBackend::TextStore(mut store, _lock) => { diff --git a/crates/uv/src/commands/auth/logout.rs b/crates/uv/src/commands/auth/logout.rs index e6c2ac7a6..fe7aef71f 100644 --- a/crates/uv/src/commands/auth/logout.rs +++ b/crates/uv/src/commands/auth/logout.rs @@ -4,13 +4,12 @@ use anyhow::{Context, Result, bail}; use owo_colors::OwoColorize; use uv_auth::Service; +use uv_auth::store::AuthBackend; use uv_auth::{Credentials, TextCredentialStore}; -use uv_configuration::KeyringProviderType; use uv_distribution_types::IndexUrl; use uv_pep508::VerbatimUrl; use uv_preview::Preview; -use crate::commands::auth::AuthBackend; use crate::{commands::ExitStatus, printer::Printer}; /// Logout from a service. @@ -19,11 +18,10 @@ use crate::{commands::ExitStatus, printer::Printer}; pub(crate) async fn logout( service: Service, username: Option, - keyring_provider: Option, printer: Printer, preview: Preview, ) -> Result { - let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?; + let backend = AuthBackend::from_settings(preview)?; // TODO(zanieb): Use a shared abstraction across `login` and `logout`? let url = service.url().clone(); @@ -55,7 +53,7 @@ pub(crate) async fn logout( // TODO(zanieb): Consider exhaustively logging out from all backends match backend { - AuthBackend::Keyring(provider) => { + AuthBackend::System(provider) => { provider .remove(&url, &username) .await diff --git a/crates/uv/src/commands/auth/mod.rs b/crates/uv/src/commands/auth/mod.rs index a781ef8b9..e446e2b1b 100644 --- a/crates/uv/src/commands/auth/mod.rs +++ b/crates/uv/src/commands/auth/mod.rs @@ -1,43 +1,4 @@ -use uv_auth::{KeyringProvider, TextCredentialStore, TomlCredentialError}; -use uv_configuration::KeyringProviderType; -use uv_fs::LockedFile; -use uv_preview::Preview; - pub(crate) mod dir; pub(crate) mod login; pub(crate) mod logout; pub(crate) mod token; - -/// The storage backend to use in `uv auth` commands. -enum AuthBackend { - Keyring(KeyringProvider), - TextStore(TextCredentialStore, LockedFile), -} - -impl AuthBackend { - fn from_settings( - keyring: Option<&KeyringProviderType>, - preview: Preview, - ) -> Result { - // For keyring providers, we only support persistence via the native keyring right now - if let Some(keyring) = match keyring { - Some(provider @ KeyringProviderType::Native) => provider.to_provider(&preview), - _ => None, - } { - return Ok(Self::Keyring(keyring)); - } - - // Otherwise, we'll use the plain text credential store - let path = TextCredentialStore::default_file()?; - match TextCredentialStore::read(&path) { - Ok((store, lock)) => Ok(Self::TextStore(store, lock)), - Err(TomlCredentialError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => { - Ok(Self::TextStore( - TextCredentialStore::default(), - TextCredentialStore::lock(&path)?, - )) - } - Err(err) => Err(err), - } - } -} diff --git a/crates/uv/src/commands/auth/token.rs b/crates/uv/src/commands/auth/token.rs index 6078de301..fa022c196 100644 --- a/crates/uv/src/commands/auth/token.rs +++ b/crates/uv/src/commands/auth/token.rs @@ -4,21 +4,19 @@ use anyhow::{Result, bail}; use uv_auth::Credentials; use uv_auth::Service; -use uv_configuration::KeyringProviderType; +use uv_auth::store::AuthBackend; use uv_preview::Preview; -use crate::commands::auth::AuthBackend; use crate::{commands::ExitStatus, printer::Printer}; /// Show the token that will be used for a service. pub(crate) async fn token( service: Service, username: Option, - keyring_provider: Option, printer: Printer, preview: Preview, ) -> Result { - let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?; + let backend = AuthBackend::from_settings(preview)?; let url = service.url(); // Extract credentials from URL if present @@ -43,7 +41,7 @@ pub(crate) async fn token( }; let credentials = match &backend { - AuthBackend::Keyring(provider) => provider + AuthBackend::System(provider) => provider .fetch(url, Some(&username)) .await .ok_or_else(|| anyhow::anyhow!("Failed to fetch credentials for {display_url}"))?, diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs index db159db09..f9de28408 100644 --- a/crates/uv/src/commands/publish.rs +++ b/crates/uv/src/commands/publish.rs @@ -12,7 +12,6 @@ use uv_cache::Cache; use uv_client::{AuthIntegration, BaseClient, BaseClientBuilder, RegistryClientBuilder}; use uv_configuration::{KeyringProviderType, TrustedPublishing}; use uv_distribution_types::{IndexCapabilities, IndexLocations, IndexUrl}; -use uv_preview::Preview; use uv_publish::{ CheckUrlClient, TrustedPublishResult, check_trusted_publishing, files_for_publishing, upload, }; @@ -35,7 +34,6 @@ pub(crate) async fn publish( index_locations: IndexLocations, cache: &Cache, printer: Printer, - preview: Preview, ) -> Result { if client_builder.is_offline() { bail!("Unable to publish files in offline mode"); @@ -85,7 +83,6 @@ pub(crate) async fn publish( check_url.as_ref(), Prompt::Enabled, printer, - preview, ) .await?; @@ -200,7 +197,6 @@ async fn gather_credentials( check_url: Option<&IndexUrl>, prompt: Prompt, printer: Printer, - preview: Preview, ) -> Result<(DisplaySafeUrl, Credentials)> { // Support reading username and password from the URL, for symmetry with the index API. if let Some(url_password) = publish_url.password() { @@ -284,7 +280,7 @@ async fn gather_credentials( // If applicable, fetch the password from the keyring eagerly to avoid user confusion about // missing keyring entries later. - if let Some(provider) = keyring_provider.to_provider(&preview) { + if let Some(provider) = keyring_provider.to_provider() { if password.is_none() { if let Some(username) = &username { debug!("Fetching password from keyring"); @@ -354,7 +350,6 @@ mod tests { None, Prompt::Disabled, Printer::Quiet, - Preview::default(), ) .await } diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index c1aaeb76e..8016c7349 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -451,7 +451,6 @@ async fn run(mut cli: Cli) -> Result { args.username, args.password, args.token, - args.keyring_provider, printer, globals.preview, ) @@ -464,14 +463,7 @@ async fn run(mut cli: Cli) -> Result { let args = settings::AuthLogoutSettings::resolve(args, filesystem); show_settings!(args); - commands::auth_logout( - args.service, - args.username, - args.keyring_provider, - printer, - globals.preview, - ) - .await + commands::auth_logout(args.service, args.username, printer, globals.preview).await } Commands::Auth(AuthNamespace { command: AuthCommand::Token(args), @@ -480,14 +472,7 @@ async fn run(mut cli: Cli) -> Result { let args = settings::AuthTokenSettings::resolve(args, filesystem); show_settings!(args); - commands::auth_token( - args.service, - args.username, - args.keyring_provider, - printer, - globals.preview, - ) - .await + commands::auth_token(args.service, args.username, printer, globals.preview).await } Commands::Auth(AuthNamespace { command: AuthCommand::Dir, @@ -1689,7 +1674,6 @@ async fn run(mut cli: Cli) -> Result { index_locations, &cache, printer, - globals.preview, ) .await } diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index cb14c0bcf..3d3cc55fc 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -3490,28 +3490,14 @@ impl PublishSettings { pub(crate) struct AuthLogoutSettings { pub(crate) service: Service, pub(crate) username: Option, - - // Both CLI and configuration. - pub(crate) keyring_provider: Option, } impl AuthLogoutSettings { /// Resolve the [`AuthLogoutSettings`] from the CLI and filesystem configuration. - pub(crate) fn resolve(args: AuthLogoutArgs, filesystem: Option) -> Self { - let Options { top_level, .. } = filesystem - .map(FilesystemOptions::into_options) - .unwrap_or_default(); - - let ResolverInstallerSchema { - keyring_provider, .. - } = top_level; - - let keyring_provider = args.keyring_provider.combine(keyring_provider); - + pub(crate) fn resolve(args: AuthLogoutArgs, _filesystem: Option) -> Self { Self { service: args.service, username: args.username, - keyring_provider, } } } @@ -3521,28 +3507,14 @@ impl AuthLogoutSettings { pub(crate) struct AuthTokenSettings { pub(crate) service: Service, pub(crate) username: Option, - - // Both CLI and configuration. - pub(crate) keyring_provider: Option, } impl AuthTokenSettings { /// Resolve the [`AuthTokenSettings`] from the CLI and filesystem configuration. - pub(crate) fn resolve(args: AuthTokenArgs, filesystem: Option) -> Self { - let Options { top_level, .. } = filesystem - .map(FilesystemOptions::into_options) - .unwrap_or_default(); - - let ResolverInstallerSchema { - keyring_provider, .. - } = top_level; - - let keyring_provider = args.keyring_provider.combine(keyring_provider); - + pub(crate) fn resolve(args: AuthTokenArgs, _filesystem: Option) -> Self { Self { service: args.service, username: args.username, - keyring_provider, } } } @@ -3554,30 +3526,16 @@ pub(crate) struct AuthLoginSettings { pub(crate) username: Option, pub(crate) password: Option, pub(crate) token: Option, - - // Both CLI and configuration. - pub(crate) keyring_provider: Option, } impl AuthLoginSettings { /// Resolve the [`AuthLoginSettings`] from the CLI and filesystem configuration. - pub(crate) fn resolve(args: AuthLoginArgs, filesystem: Option) -> Self { - let Options { top_level, .. } = filesystem - .map(FilesystemOptions::into_options) - .unwrap_or_default(); - - let ResolverInstallerSchema { - keyring_provider, .. - } = top_level; - - let keyring_provider = args.keyring_provider.combine(keyring_provider); - + pub(crate) fn resolve(args: AuthLoginArgs, _filesystem: Option) -> Self { Self { service: args.service, username: args.username, password: args.password, token: args.token, - keyring_provider, } } } diff --git a/crates/uv/tests/it/auth.rs b/crates/uv/tests/it/auth.rs index f1e9ed7d2..a568c3a82 100644 --- a/crates/uv/tests/it/auth.rs +++ b/crates/uv/tests/it/auth.rs @@ -1,16 +1,16 @@ -#[cfg(feature = "keyring-tests")] +#[cfg(feature = "native-auth")] use anyhow::Result; use assert_cmd::assert::OutputAssertExt; -#[cfg(feature = "keyring-tests")] +#[cfg(feature = "native-auth")] use assert_fs::{fixture::PathChild, prelude::FileWriteStr}; +#[cfg(feature = "native-auth")] use uv_static::EnvVars; -use crate::common::venv_bin_path; use crate::common::{TestContext, uv_snapshot}; #[test] -#[cfg(feature = "keyring-tests")] -fn add_package_native_keyring() -> Result<()> { +#[cfg(feature = "native-auth")] +fn add_package_native_auth() -> Result<()> { let context = TestContext::new("3.12").with_real_home(); // Clear state before the test @@ -19,8 +19,7 @@ fn add_package_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .status()?; // Configure `pyproject.toml` with native keyring provider. @@ -31,20 +30,17 @@ fn add_package_native_keyring() -> Result<()> { version = "1.0.0" requires-python = ">=3.11, <4" dependencies = [] - - [tool.uv] - keyring-provider = "native" "# })?; // Try to add a package without credentials. - uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple"), @r" + uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. × No solution found when resolving dependencies: ╰─▶ Because anyio was not found in the package registry and your project depends on anyio, we can conclude that your project's requirements are unsatisfiable. @@ -59,26 +55,26 @@ fn add_package_native_keyring() -> Result<()> { .arg("--username") .arg("public") .arg("--password") - .arg("heron"), @r" + .arg("heron") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth " ); // Try to add the original package without credentials again. This should use // credentials storied in the system keyring. - uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple"), @r" + uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. × No solution found when resolving dependencies: ╰─▶ Because anyio was not found in the package registry and your project depends on anyio, we can conclude that your project's requirements are unsatisfiable. @@ -91,25 +87,25 @@ fn add_package_native_keyring() -> Result<()> { uv_snapshot!(context.auth_logout() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") - .arg("public"), @r" + .arg("public") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Removed credentials for public@https://pypi-proxy.fly.dev/basic-auth " ); // Authentication should fail again - uv_snapshot!(context.add().arg("iniconfig").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple"), @r" + uv_snapshot!(context.add().arg("iniconfig").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. × No solution found when resolving dependencies: ╰─▶ Because iniconfig was not found in the package registry and your project depends on iniconfig, we can conclude that your project's requirements are unsatisfiable. @@ -122,8 +118,8 @@ fn add_package_native_keyring() -> Result<()> { } #[test] -#[cfg(feature = "keyring-tests")] -fn token_native_keyring() -> Result<()> { +#[cfg(feature = "native-auth")] +fn token_native_auth() -> Result<()> { let context = TestContext::new_with_versions(&[]).with_real_home(); // Clear state before the test @@ -132,21 +128,18 @@ fn token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .status()?; // Without persisted credentials uv_snapshot!(context.auth_token() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -155,14 +148,12 @@ fn token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -173,14 +164,12 @@ fn token_native_keyring() -> Result<()> { .arg("public") .arg("--password") .arg("heron") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth " ); @@ -190,14 +179,12 @@ fn token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -205,14 +192,12 @@ fn token_native_keyring() -> Result<()> { // TODO(zanieb): Add a hint here if we can? uv_snapshot!(context.auth_token() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -222,14 +207,12 @@ fn token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("private") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for private@https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -238,14 +221,12 @@ fn token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--token") .arg("heron") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for https://pypi-proxy.fly.dev/basic-auth " ); @@ -253,14 +234,12 @@ fn token_native_keyring() -> Result<()> { // Retrieve the token without a username uv_snapshot!(context.auth_token() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -274,14 +253,12 @@ fn token_native_keyring() -> Result<()> { // Retrieve token using URL with embedded username (no --username needed) uv_snapshot!(context.auth_token() .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple "); @@ -290,14 +267,12 @@ fn token_native_keyring() -> Result<()> { .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("different") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Cannot specify a username both via the URL and CLI; found `--username different` and `public` "); @@ -305,90 +280,8 @@ fn token_native_keyring() -> Result<()> { } #[test] -fn token_subprocess_keyring() { - let context = TestContext::new("3.12"); - - // Without a keyring on the PATH - uv_snapshot!(context.auth_token() - .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("subprocess"), @r" - success: false - exit_code: 2 - ----- stdout ----- - - ----- stderr ----- - error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple - " - ); - - // Install our keyring plugin - context - .pip_install() - .arg( - context - .workspace_root - .join("scripts") - .join("packages") - .join("keyring_test_plugin"), - ) - .assert() - .success(); - - // Without credentials available - uv_snapshot!(context.auth_token() - .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("subprocess") - .env(EnvVars::PATH, venv_bin_path(&context.venv)), @r" - success: false - exit_code: 2 - ----- stdout ----- - - ----- stderr ----- - error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple - " - ); - - // Without a username - // TODO(zanieb): Add a hint here if we can? - uv_snapshot!(context.auth_token() - .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("subprocess") - .env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#) - .env(EnvVars::PATH, venv_bin_path(&context.venv)), @r" - success: false - exit_code: 2 - ----- stdout ----- - - ----- stderr ----- - error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple - " - ); - - // With the correct username - uv_snapshot!(context.auth_token() - .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("subprocess") - .arg("--username") - .arg("public") - .env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#) - .env(EnvVars::PATH, venv_bin_path(&context.venv)), @r" - success: false - exit_code: 2 - ----- stdout ----- - - ----- stderr ----- - error: Cannot specify a username both via the URL and CLI; found `--username public` and `public` - " - ); -} - -#[test] -#[cfg(feature = "keyring-tests")] -fn login_native_keyring() -> Result<()> { +#[cfg(feature = "native-auth")] +fn login_native_auth() -> Result<()> { let context = TestContext::new_with_versions(&[]).with_real_home(); // Clear state before the test @@ -397,8 +290,7 @@ fn login_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .status()?; // Without a service name @@ -419,14 +311,12 @@ fn login_native_keyring() -> Result<()> { // Without a username (or token) uv_snapshot!(context.auth_login() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: No username provided; did you mean to provide `--username` or `--token`? "); @@ -435,14 +325,12 @@ fn login_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: No password provided; did you mean to provide `--password` or `--token`? "); @@ -453,14 +341,12 @@ fn login_native_keyring() -> Result<()> { .arg("public") .arg("--password") .arg("heron") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth " ); @@ -469,8 +355,8 @@ fn login_native_keyring() -> Result<()> { } #[test] -#[cfg(feature = "keyring-tests")] -fn login_token_native_keyring() -> Result<()> { +#[cfg(feature = "native-auth")] +fn login_token_native_auth() -> Result<()> { let context = TestContext::new_with_versions(&[]).with_real_home(); // Clear state before the test @@ -479,8 +365,7 @@ fn login_token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("__token__") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .status()?; // Successful with token @@ -488,14 +373,12 @@ fn login_token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--token") .arg("test-token") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for https://pypi-proxy.fly.dev/basic-auth " ); @@ -504,8 +387,8 @@ fn login_token_native_keyring() -> Result<()> { } #[test] -#[cfg(feature = "keyring-tests")] -fn logout_native_keyring() -> Result<()> { +#[cfg(feature = "native-auth")] +fn logout_native_auth() -> Result<()> { let context = TestContext::new_with_versions(&[]).with_real_home(); // Clear state before the test @@ -514,8 +397,7 @@ fn logout_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .status()?; // Without a service name @@ -536,14 +418,12 @@ fn logout_native_keyring() -> Result<()> { // Logout before logging in uv_snapshot!(context.auth_logout() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Removed credentials for https://pypi-proxy.fly.dev/basic-auth "); @@ -552,14 +432,12 @@ fn logout_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Unable to remove credentials for public@https://pypi-proxy.fly.dev/basic-auth Caused by: No matching entry found in secure storage "); @@ -571,14 +449,12 @@ fn logout_native_keyring() -> Result<()> { .arg("public") .arg("--password") .arg("heron") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for public@https://pypi-proxy.fly.dev/basic-auth " ); @@ -587,14 +463,12 @@ fn logout_native_keyring() -> Result<()> { // TODO(zanieb): Add a hint here if we can? uv_snapshot!(context.auth_logout() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Unable to remove credentials for https://pypi-proxy.fly.dev/basic-auth Caused by: No matching entry found in secure storage "); @@ -604,14 +478,12 @@ fn logout_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("public") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Removed credentials for public@https://pypi-proxy.fly.dev/basic-auth "); @@ -623,22 +495,19 @@ fn logout_native_keyring() -> Result<()> { .arg("public") .arg("--password") .arg("heron") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .assert() .success(); // Logout with a username in the URL uv_snapshot!(context.auth_logout() .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Removed credentials for public@https://pypi-proxy.fly.dev/basic-auth "); @@ -647,14 +516,12 @@ fn logout_native_keyring() -> Result<()> { .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") .arg("--username") .arg("foo") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Cannot specify a username both via the URL and CLI; found `--username foo` and `public` "); @@ -663,14 +530,12 @@ fn logout_native_keyring() -> Result<()> { .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple") .arg("--token") .arg("foo") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: When using `--token`, a username cannot not be provided; found: public "); @@ -678,16 +543,15 @@ fn logout_native_keyring() -> Result<()> { } #[test] -#[cfg(feature = "keyring-tests")] -fn logout_token_native_keyring() -> Result<()> { +#[cfg(feature = "native-auth")] +fn logout_token_native_auth() -> Result<()> { let context = TestContext::new_with_versions(&[]).with_real_home(); // Clear state before the test context .auth_logout() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native") + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth") .status()?; // Login with a token @@ -695,14 +559,12 @@ fn logout_token_native_keyring() -> Result<()> { .arg("https://pypi-proxy.fly.dev/basic-auth/simple") .arg("--token") .arg("test-token") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for https://pypi-proxy.fly.dev/basic-auth " ); @@ -710,14 +572,12 @@ fn logout_token_native_keyring() -> Result<()> { // Logout without a username uv_snapshot!(context.auth_logout() .arg("https://pypi-proxy.fly.dev/basic-auth/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Removed credentials for https://pypi-proxy.fly.dev/basic-auth "); @@ -725,8 +585,8 @@ fn logout_token_native_keyring() -> Result<()> { } #[test] -#[cfg(feature = "keyring-tests")] -fn login_native_keyring_url() { +#[cfg(feature = "native-auth")] +fn login_native_auth_url() { let context = TestContext::new_with_versions(&[]).with_real_home(); // A domain-only service name gets https:// prepended @@ -736,14 +596,12 @@ fn login_native_keyring_url() { .arg("test") .arg("--password") .arg("test") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for test@https://example.com/ "); @@ -754,8 +612,7 @@ fn login_native_keyring_url() { .arg("test") .arg("--password") .arg("test") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- @@ -772,14 +629,12 @@ fn login_native_keyring_url() { .arg("test") .arg("--password") .arg("test") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for test@https://example.com/ "); @@ -790,14 +645,12 @@ fn login_native_keyring_url() { .arg("test") .arg("--password") .arg("test") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for test@https://example.com/ "); @@ -808,8 +661,7 @@ fn login_native_keyring_url() { .arg("test") .arg("--password") .arg("test") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- @@ -823,14 +675,12 @@ fn login_native_keyring_url() { // URL with embedded credentials works uv_snapshot!(context.auth_login() .arg("https://test:password@example.com/simple") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for test@https://example.com/ "); @@ -839,14 +689,12 @@ fn login_native_keyring_url() { .arg("https://test@example.com/simple") .arg("--password") .arg("password") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. Stored credentials for test@https://example.com/ "); @@ -857,14 +705,12 @@ fn login_native_keyring_url() { .arg("different") .arg("--password") .arg("password") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Cannot specify a username both via the URL and CLI; found `--username different` and `test` "); @@ -873,14 +719,12 @@ fn login_native_keyring_url() { .arg("https://test:password@example.com/simple") .arg("--password") .arg("different") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: Cannot specify a password both via the URL and CLI "); @@ -889,14 +733,12 @@ fn login_native_keyring_url() { .arg("https://test:password@example.com/simple") .arg("--token") .arg("some-token") - .arg("--keyring-provider") - .arg("native"), @r" + .env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- - warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning. error: When using `--token`, a username cannot not be provided; found: test "); } diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index cc2a1ea50..fb0d9afd0 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -7684,7 +7684,7 @@ fn preview_features() { show_settings: true, preview: Preview { flags: PreviewFeatures( - PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_KEYRING, + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH, ), }, python_preference: Managed, @@ -7908,7 +7908,7 @@ fn preview_features() { show_settings: true, preview: Preview { flags: PreviewFeatures( - PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_KEYRING, + PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH, ), }, python_preference: Managed, diff --git a/docs/concepts/authentication/cli.md b/docs/concepts/authentication/cli.md index 6178b231f..5c8890f64 100644 --- a/docs/concepts/authentication/cli.md +++ b/docs/concepts/authentication/cli.md @@ -52,9 +52,7 @@ $ uv auth token --username foo example.com ## Configuring the storage backend -By default, credentials are persisted in plain text to the uv -[credentials file](./http.md#the-uv-credentials-file). +Credentials are persisted to the uv [credentials store](./http.md#the-uv-credentials-store). -If the [native keyring provider](./http.md#the-native-keyring-provider) is enabled, it will be used -instead, and the credentials will be stored in a secure system store. The native keyring is -currently experimental, but will become the default in the future. +By default, credentials are written to a plaintext file. An encrypted system-native storage backend +can be enabled with `UV_PREVIEW_FEATURES=native-auth`. diff --git a/docs/concepts/authentication/http.md b/docs/concepts/authentication/http.md index d9f1835a1..cb60f120f 100644 --- a/docs/concepts/authentication/http.md +++ b/docs/concepts/authentication/http.md @@ -6,7 +6,7 @@ Authentication can come from the following sources, in order of precedence: - The URL, e.g., `https://:@/...` - A [netrc](#netrc-files) configuration file -- The uv credentials file +- The uv credentials store - A [keyring provider](#keyring-providers) (off by default) Authentication may be used for hosts specified in the following contexts: @@ -25,45 +25,39 @@ for storing credentials on a system. Reading credentials from `.netrc` files is always enabled. The target file path will be loaded from the `NETRC` environment variable if defined, falling back to `~/.netrc` if not. -## The uv credentials file +## The uv credentials store -uv will read credentials from `~/.local/share/uv/credentials/credentials.toml`. This file is -currently not intended to be edited manually. +uv can read and write credentials from a store using the [`uv auth` commands](./cli.md). -To add or remove credentials, use the [`uv auth` commands](./cli.md). - -## Keyring providers - -A keyring provider typically fetches credentials from an operating system store. - -The keyring providers are not used by default. - -### The 'subprocess' keyring provider - -The 'subprocess' keyring provider invokes the `keyring` command to fetch credentials. - -The expected interface for this is based on the popular [keyring](https://github.com/jaraco/keyring) -Python package. Similar support is built-in to pip. - -Set `--keyring-provider subprocess`, `UV_KEYRING_PROVIDER=subprocess`, or -`tool.uv.keyring-provider = "subprocess"` to use the provider. - -### The 'native' keyring provider +Credentials are stored in a plaintext file in uv's state directory, e.g., +`~/.local/share/uv/credentials/credentials.toml` on Unix. This file is currently not intended to be +edited manually. !!! note - The native keyring provider is in [preview](../preview.md) — it is still experimental and being - actively developed. + A secure, system native storage mechanism is in [preview](../preview.md) — it is still + experimental and being actively developed. In the future, this will become the default storage + mechanism. -The native keyring provider uses the secret storage mechanism native to your operating system. On -macOS, it uses the Keychain Services. On Windows, it uses the Windows Credential Manager. On Linux, -it uses the DBus-based Secret Service API. + When enabled, uv will use the secret storage mechanism native to your operating system. On + macOS, it uses the Keychain Services. On Windows, it uses the Windows Credential Manager. On + Linux, it uses the DBus-based Secret Service API. -Currently, uv only searches the native keyring provider for credentials it has added to the secret -store. To add or remove credentials, use the [`uv auth` commands](./cli.md). + Currently, uv only searches the native store for credentials it has added to the secret store — + it will not retrieve credentials persisted by other applications. -Set `--keyring-provider native`, `UV_KEYRING_PROVIDER=native`, or -`tool.uv.keyring-provider = "native"` to use the provider. + Set `UV_PREVIEW_FEATURES=native-auth` to use this storage mechanism. + +## Keyring providers + +A keyring provider is a concept from `pip` allowing retrieval of credentials from an interface +matching the popular [keyring](https://github.com/jaraco/keyring) Python package. + +The "subprocess" keyring provider invokes the `keyring` command to fetch credentials. uv does not +support additional keyring provider types at this time. + +Set `--keyring-provider subprocess`, `UV_KEYRING_PROVIDER=subprocess`, or +`tool.uv.keyring-provider = "subprocess"` to use the provider. ## Persistence of credentials diff --git a/docs/concepts/preview.md b/docs/concepts/preview.md index 986f7674b..effb389a8 100644 --- a/docs/concepts/preview.md +++ b/docs/concepts/preview.md @@ -70,6 +70,8 @@ The following preview features are available: - `python-upgrade`: Allows [transparent Python version upgrades](./python-versions.md#upgrading-python-versions). - `format`: Allows using `uv format`. +- `native-auth`: Enables storage of credentials in a + [system-native location](../concepts/authentication/http.md#the-uv-credentials-store). ## Disabling preview features diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 3ba8eb2c9..3f945e73c 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -94,7 +94,6 @@ uv auth login [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

@@ -168,7 +167,6 @@ uv auth logout [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

@@ -238,7 +236,6 @@ uv auth token [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

@@ -447,7 +444,6 @@ uv run [OPTIONS] [COMMAND]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -853,7 +849,6 @@ uv add [OPTIONS] >

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -1054,7 +1049,6 @@ uv remove [OPTIONS] ...

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -1237,7 +1231,6 @@ uv version [OPTIONS] [VALUE]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -1436,7 +1429,6 @@ uv sync [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -1682,7 +1674,6 @@ uv lock [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

This option is only used when building source distributions.

@@ -1862,7 +1853,6 @@ uv export [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

This option is only used when building source distributions.

@@ -2057,7 +2047,6 @@ uv tree [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

This option is only used when building source distributions.

@@ -2401,7 +2390,6 @@ uv tool run [OPTIONS] [COMMAND]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -2623,7 +2611,6 @@ uv tool install [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -2837,7 +2824,6 @@ uv tool upgrade [OPTIONS] ...

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -4058,7 +4044,6 @@ uv pip compile [OPTIONS] >

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

This option is only used when building source distributions.

@@ -4354,7 +4339,6 @@ uv pip sync [OPTIONS] ...

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -4628,7 +4612,6 @@ uv pip install [OPTIONS] |--editable May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

Defaults to clone (also known as Copy-on-Write) on macOS, and hardlink on Linux and Windows.

@@ -4875,7 +4858,6 @@ uv pip uninstall [OPTIONS] >

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

@@ -5054,7 +5036,6 @@ uv pip list [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

@@ -5230,7 +5211,6 @@ uv pip tree [OPTIONS]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

@@ -5472,7 +5452,6 @@ uv venv [OPTIONS] [PATH]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

This option is only used for installing seed packages.

@@ -5629,7 +5608,6 @@ uv build [OPTIONS] [SRC]

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup

The method to use when installing packages from the global cache.

This option is only used when building source distributions.

@@ -5782,7 +5760,6 @@ uv publish --publish-url https://upload.pypi.org/legacy/ --check-url https://pyp

May also be set with the UV_KEYRING_PROVIDER environment variable.

Possible values:

  • disabled: Do not use keyring for credential lookup
  • -
  • native: Use a native integration with the system keychain for credential lookup
  • subprocess: Use the keyring command for credential lookup
--managed-python

Require use of uv-managed Python versions.

By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.

diff --git a/uv.schema.json b/uv.schema.json index e062fca5b..655ce6a84 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -1144,11 +1144,6 @@ "type": "string", "const": "disabled" }, - { - "description": "Use a native integration with the system keychain for credential lookup.", - "type": "string", - "const": "native" - }, { "description": "Use the `keyring` command for credential lookup.", "type": "string",