Skip to content
Merged
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
93 changes: 9 additions & 84 deletions crates/uv/src/commands/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,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_pep508::VerbatimUrl;
use uv_publish::{
CheckUrlClient, FormMetadata, PublishError, TrustedPublishResult, check_trusted_publishing,
files_for_publishing, upload,
Expand Down Expand Up @@ -75,16 +74,15 @@ pub(crate) async fn publish(
.publish_url
.clone()
.with_context(|| format!("Index is missing a publish URL: `{index_name}`"))?;
let check_url = index.url.clone();
(publish_url, Some(check_url))
} else if token_store.is_known_url(&publish_url) {
// If the user is publishing to a known index, construct the check URL from the publish
// URL.
let check_url = check_url.or_else(|| {
infer_check_url(&publish_url)
.inspect(|check_url| debug!("Inferred check URL: {check_url}"))
});
(publish_url, check_url)

// pyx has the same behavior as PyPI where uploads of identical
// files + contents are idempotent, so we don't need to pre-check.
if token_store.is_known_url(&publish_url) {
(publish_url, None)
} else {
let check_url = index.url.clone();
(publish_url, Some(check_url))
}
} else {
(publish_url, check_url)
};
Expand Down Expand Up @@ -439,50 +437,6 @@ fn prompt_username_and_password() -> Result<(Option<String>, Option<String>)> {
Ok((Some(username), Some(password)))
}

/// Construct a Simple Index URL from a publish URL, if possible.
///
/// Matches against a publish URL of the form `/v1/upload/{workspace}/{registry}` and returns
/// `/simple/{workspace}/{registry}`.
fn infer_check_url(publish_url: &DisplaySafeUrl) -> Option<IndexUrl> {
let mut segments = publish_url.path_segments()?;

let v1 = segments.next()?;
if v1 != "v1" {
return None;
}

let upload = segments.next()?;
if upload != "upload" {
return None;
}

let workspace = segments.next()?;
if workspace.is_empty() {
return None;
}

let registry = segments.next()?;
if registry.is_empty() {
return None;
}

// Skip any empty segments (trailing slash handling)
for remaining in segments {
if !remaining.is_empty() {
return None;
}
}

// Reconstruct the URL with `/simple/{workspace}/{registry}`.
let mut check_url = publish_url.clone();
{
let mut segments = check_url.path_segments_mut().ok()?;
segments.clear();
segments.push("simple").push(workspace).push(registry);
}
Some(IndexUrl::from(VerbatimUrl::from(check_url)))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -595,33 +549,4 @@ mod tests {
@"The password can't be set both in the publish URL and in the CLI"
);
}

#[test]
fn test_infer_check_url() {
let url =
DisplaySafeUrl::from_str("https://example.com/v1/upload/workspace/registry").unwrap();
let check_url = infer_check_url(&url);
assert_eq!(
check_url,
Some(IndexUrl::from_str("https://example.com/simple/workspace/registry").unwrap())
);

let url =
DisplaySafeUrl::from_str("https://example.com/v1/upload/workspace/registry/").unwrap();
let check_url = infer_check_url(&url);
assert_eq!(
check_url,
Some(IndexUrl::from_str("https://example.com/simple/workspace/registry").unwrap())
);

let url =
DisplaySafeUrl::from_str("https://example.com/upload/workspace/registry").unwrap();
let check_url = infer_check_url(&url);
assert_eq!(check_url, None);

let url = DisplaySafeUrl::from_str("https://example.com/upload/workspace/registry/package")
.unwrap();
let check_url = infer_check_url(&url);
assert_eq!(check_url, None);
}
}
Loading