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
11 changes: 10 additions & 1 deletion crates/uv-client/src/base_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ pub struct BaseClientBuilder<'a> {
cross_origin_credential_policy: CrossOriginCredentialsPolicy,
/// Optional custom reqwest client to use instead of creating a new one.
custom_client: Option<Client>,
/// uv subcommand in which this client is being used
subcommand: Option<Vec<String>>,
}

/// The policy for handling HTTP redirects.
Expand Down Expand Up @@ -143,6 +145,7 @@ impl Default for BaseClientBuilder<'_> {
redirect_policy: RedirectPolicy::default(),
cross_origin_credential_policy: CrossOriginCredentialsPolicy::Secure,
custom_client: None,
subcommand: None,
}
}
}
Expand Down Expand Up @@ -276,6 +279,12 @@ impl<'a> BaseClientBuilder<'a> {
self
}

#[must_use]
pub fn subcommand(mut self, subcommand: Vec<String>) -> Self {
self.subcommand = Some(subcommand);
self
}

pub fn is_native_tls(&self) -> bool {
self.native_tls
}
Expand Down Expand Up @@ -358,7 +367,7 @@ impl<'a> BaseClientBuilder<'a> {
let mut user_agent_string = format!("uv/{}", version());

// Add linehaul metadata.
let linehaul = LineHaul::new(self.markers, self.platform);
let linehaul = LineHaul::new(self.markers, self.platform, self.subcommand.clone());
if let Ok(output) = serde_json::to_string(&linehaul) {
let _ = write!(user_agent_string, " {output}");
}
Expand Down
8 changes: 7 additions & 1 deletion crates/uv-client/src/linehaul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use uv_version::version;
pub struct Installer {
pub name: Option<String>,
pub version: Option<String>,
pub subcommand: Option<Vec<String>>,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
Expand Down Expand Up @@ -63,7 +64,11 @@ pub struct LineHaul {
impl LineHaul {
/// Initializes Linehaul information based on PEP 508 markers.
#[instrument(name = "linehaul", skip_all)]
pub fn new(markers: Option<&MarkerEnvironment>, platform: Option<&Platform>) -> Self {
pub fn new(
markers: Option<&MarkerEnvironment>,
platform: Option<&Platform>,
subcommand: Option<Vec<String>>,
) -> Self {
// https://github.com/pypa/pip/blob/24.0/src/pip/_internal/network/session.py#L87
let looks_like_ci = [
EnvVars::BUILD_BUILDID,
Expand Down Expand Up @@ -123,6 +128,7 @@ impl LineHaul {
installer: Option::from(Installer {
name: Some("uv".to_string()),
version: Some(version().to_string()),
subcommand,
}),
python: markers.map(|markers| markers.python_full_version().version.to_string()),
implementation: Option::from(Implementation {
Expand Down
72 changes: 68 additions & 4 deletions crates/uv-client/tests/it/user_agent_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,70 @@ async fn test_user_agent_has_version() -> Result<()> {
assert_json_snapshot!(&linehaul.installer, @r#"
{
"name": "uv",
"version": "[VERSION]"
"version": "[VERSION]",
"subcommand": null
}
"#);
});

// Wait for the server task to complete, to be a good citizen.
let _ = server_task.await?;

Ok(())
}

#[tokio::test]
async fn test_user_agent_has_subcommand() -> Result<()> {
// Initialize dummy http server
let (server_task, addr) = start_http_user_agent_server().await?;

// Initialize uv-client
let cache = Cache::temp()?.init()?;
let client = RegistryClientBuilder::new(
BaseClientBuilder::default().subcommand(vec!["foo".to_owned(), "bar".to_owned()]),
cache,
)
.build();

// Send request to our dummy server
let url = DisplaySafeUrl::from_str(&format!("http://{addr}"))?;
let res = client
.cached_client()
.uncached()
.for_host(&url)
.get(Url::from(url))
.send()
.await?;

// Check the HTTP status
assert!(res.status().is_success());

// Check User Agent
let body = res.text().await?;

let (uv_version, uv_linehaul) = body
.split_once(' ')
.expect("Failed to split User-Agent header");

// Deserializing Linehaul
let linehaul: LineHaul = serde_json::from_str(uv_linehaul)?;

// Assert linehaul user agent
let filters = vec![(version(), "[VERSION]")];
with_settings!({
filters => filters
}, {
// Assert uv version
assert_snapshot!(uv_version, @"uv/[VERSION]");
// Assert linehaul json
assert_json_snapshot!(&linehaul.installer, @r#"
{
"name": "uv",
"version": "[VERSION]",
"subcommand": [
"foo",
"bar"
]
}
"#);
});
Expand Down Expand Up @@ -152,11 +215,12 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
assert_json_snapshot!(&linehaul, {
".distro" => "[distro]",
".ci" => "[ci]"
}, @r###"
}, @r#"
{
"installer": {
"name": "uv",
"version": "[VERSION]"
"version": "[VERSION]",
"subcommand": null
},
"python": "3.12.2",
"implementation": {
Expand All @@ -174,7 +238,7 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
"rustc_version": null,
"ci": "[ci]"
}
"###);
"#);
});

// Assert distro
Expand Down
Loading
Loading