mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-14 09:45:48 +00:00
Use token store credentials for uv publish (#15759)
## Summary Running `uv publish` to pyx should re-use the already-stored token rather than prompting for credentials. Closes https://github.com/astral-sh/uv/issues/15758.
This commit is contained in:
parent
cd49e1d11f
commit
5494645fba
1 changed files with 29 additions and 3 deletions
|
|
@ -7,7 +7,7 @@ use console::Term;
|
||||||
use owo_colors::{AnsiColors, OwoColorize};
|
use owo_colors::{AnsiColors, OwoColorize};
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
use tracing::{debug, info, trace};
|
use tracing::{debug, info, trace};
|
||||||
use uv_auth::{Credentials, PyxTokenStore};
|
use uv_auth::{Credentials, DEFAULT_TOLERANCE_SECS, PyxTokenStore};
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{AuthIntegration, BaseClient, BaseClientBuilder, RegistryClientBuilder};
|
use uv_client::{AuthIntegration, BaseClient, BaseClientBuilder, RegistryClientBuilder};
|
||||||
use uv_configuration::{KeyringProviderType, TrustedPublishing};
|
use uv_configuration::{KeyringProviderType, TrustedPublishing};
|
||||||
|
|
@ -84,16 +84,22 @@ pub(crate) async fn publish(
|
||||||
.clone()
|
.clone()
|
||||||
.auth_integration(AuthIntegration::NoAuthMiddleware)
|
.auth_integration(AuthIntegration::NoAuthMiddleware)
|
||||||
.wrap_existing(&upload_client);
|
.wrap_existing(&upload_client);
|
||||||
|
|
||||||
// We're only checking a single URL and one at a time, so 1 permit is sufficient
|
// We're only checking a single URL and one at a time, so 1 permit is sufficient
|
||||||
let download_concurrency = Arc::new(Semaphore::new(1));
|
let download_concurrency = Arc::new(Semaphore::new(1));
|
||||||
|
|
||||||
|
// Load credentials from the token store.
|
||||||
|
let token_store = PyxTokenStore::from_settings()?;
|
||||||
|
|
||||||
let (publish_url, credentials) = gather_credentials(
|
let (publish_url, credentials) = gather_credentials(
|
||||||
publish_url,
|
publish_url,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
trusted_publishing,
|
trusted_publishing,
|
||||||
keyring_provider,
|
keyring_provider,
|
||||||
|
&token_store,
|
||||||
&oidc_client,
|
&oidc_client,
|
||||||
|
&upload_client,
|
||||||
check_url.as_ref(),
|
check_url.as_ref(),
|
||||||
Prompt::Enabled,
|
Prompt::Enabled,
|
||||||
printer,
|
printer,
|
||||||
|
|
@ -151,13 +157,12 @@ pub(crate) async fn publish(
|
||||||
.map_err(|err| PublishError::PublishPrepare(file.clone(), Box::new(err)))?;
|
.map_err(|err| PublishError::PublishPrepare(file.clone(), Box::new(err)))?;
|
||||||
|
|
||||||
// Run validation checks on the file, but don't upload it (if possible).
|
// Run validation checks on the file, but don't upload it (if possible).
|
||||||
let store = PyxTokenStore::from_settings()?;
|
|
||||||
uv_publish::validate(
|
uv_publish::validate(
|
||||||
&file,
|
&file,
|
||||||
&form_metadata,
|
&form_metadata,
|
||||||
&raw_filename,
|
&raw_filename,
|
||||||
&publish_url,
|
&publish_url,
|
||||||
&store,
|
&token_store,
|
||||||
&upload_client,
|
&upload_client,
|
||||||
&credentials,
|
&credentials,
|
||||||
)
|
)
|
||||||
|
|
@ -241,7 +246,9 @@ async fn gather_credentials(
|
||||||
mut password: Option<String>,
|
mut password: Option<String>,
|
||||||
trusted_publishing: TrustedPublishing,
|
trusted_publishing: TrustedPublishing,
|
||||||
keyring_provider: KeyringProviderType,
|
keyring_provider: KeyringProviderType,
|
||||||
|
token_store: &PyxTokenStore,
|
||||||
oidc_client: &BaseClient,
|
oidc_client: &BaseClient,
|
||||||
|
base_client: &BaseClient,
|
||||||
check_url: Option<&IndexUrl>,
|
check_url: Option<&IndexUrl>,
|
||||||
prompt: Prompt,
|
prompt: Prompt,
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
|
|
@ -267,6 +274,22 @@ async fn gather_credentials(
|
||||||
.expect("Failed to clear publish URL username");
|
.expect("Failed to clear publish URL username");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user is publishing to pyx, load the credentials from the store.
|
||||||
|
if username.is_none() && password.is_none() {
|
||||||
|
if token_store.is_known_url(&publish_url) {
|
||||||
|
if let Some(token) = token_store
|
||||||
|
.access_token(
|
||||||
|
base_client.for_host(token_store.api()).raw_client(),
|
||||||
|
DEFAULT_TOLERANCE_SECS,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
debug!("Using authentication token from the store");
|
||||||
|
return Ok((publish_url, Credentials::from(token)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If applicable, attempt obtaining a token for trusted publishing.
|
// If applicable, attempt obtaining a token for trusted publishing.
|
||||||
let trusted_publishing_token = check_trusted_publishing(
|
let trusted_publishing_token = check_trusted_publishing(
|
||||||
username.as_deref(),
|
username.as_deref(),
|
||||||
|
|
@ -388,12 +411,15 @@ mod tests {
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
) -> Result<(DisplaySafeUrl, Credentials)> {
|
) -> Result<(DisplaySafeUrl, Credentials)> {
|
||||||
let client = BaseClientBuilder::default().build();
|
let client = BaseClientBuilder::default().build();
|
||||||
|
let token_store = PyxTokenStore::from_settings()?;
|
||||||
gather_credentials(
|
gather_credentials(
|
||||||
url,
|
url,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
TrustedPublishing::Never,
|
TrustedPublishing::Never,
|
||||||
KeyringProviderType::Disabled,
|
KeyringProviderType::Disabled,
|
||||||
|
&token_store,
|
||||||
|
&client,
|
||||||
&client,
|
&client,
|
||||||
None,
|
None,
|
||||||
Prompt::Disabled,
|
Prompt::Disabled,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue