diff --git a/Cargo.lock b/Cargo.lock index e121040f645d..08bd1fada869 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4464,6 +4464,7 @@ dependencies = [ "flate2", "fs-err", "futures", + "http", "ignore", "indexmap", "indicatif", diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index daf02811e819..7f589049d0f2 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -427,6 +427,11 @@ pub enum SelfCommand { pub struct SelfUpdateArgs { /// Update to the specified version. If not provided, uv will update to the latest version. pub target_version: Option, + + /// A GitHub token for authentication. + /// A token is not required but can be used to reduce the chance of encountering rate limits. + #[arg(long, env = "UV_GITHUB_TOKEN")] + pub token: Option, } #[derive(Args)] diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index 2b65aed76dbd..dcce749f4aa3 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -55,6 +55,7 @@ ctrlc = { workspace = true } flate2 = { workspace = true, default-features = false } fs-err = { workspace = true, features = ["tokio"] } futures = { workspace = true } +http = { workspace = true } indexmap = { workspace = true } indicatif = { workspace = true } indoc = { workspace = true } diff --git a/crates/uv/src/commands/self_update.rs b/crates/uv/src/commands/self_update.rs index edf6f4c43b3a..f6372d72ce06 100644 --- a/crates/uv/src/commands/self_update.rs +++ b/crates/uv/src/commands/self_update.rs @@ -11,10 +11,18 @@ use crate::commands::ExitStatus; use crate::printer::Printer; /// Attempt to update the uv binary. -pub(crate) async fn self_update(version: Option, printer: Printer) -> Result { +pub(crate) async fn self_update( + version: Option, + token: Option, + printer: Printer, +) -> Result { let mut updater = AxoUpdater::new_for("uv"); updater.disable_installer_output(); + if let Some(ref token) = token { + updater.set_github_token(token); + } + // Load the "install receipt" for the current binary. If the receipt is not found, then // uv was likely installed via a package manager. let Ok(updater) = updater.load_receipt() else { @@ -121,11 +129,25 @@ pub(crate) async fn self_update(version: Option, printer: Printer) -> Re )?; } Err(err) => { - return Err(if let AxoupdateError::Reqwest(err) = err { - WrappedReqwestError::from(err).into() + return if let AxoupdateError::Reqwest(err) = err { + if err.status() == Some(http::StatusCode::FORBIDDEN) && token.is_none() { + writeln!( + printer.stderr(), + "{}", + format_args!( + "{}{} GitHub API rate limit exceeded. Please provide a GitHub token via the {} option.", + "error".red().bold(), + ":".bold(), + "`--token`".green().bold() + ) + )?; + Ok(ExitStatus::Error) + } else { + Err(WrappedReqwestError::from(err).into()) + } } else { - err.into() - }); + Err(err.into()) + }; } } diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 9360320a3acc..0ee3a7c2720f 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -766,8 +766,12 @@ async fn run(cli: Cli) -> Result { } #[cfg(feature = "self-update")] Commands::Self_(SelfNamespace { - command: SelfCommand::Update(SelfUpdateArgs { target_version }), - }) => commands::self_update(target_version, printer).await, + command: + SelfCommand::Update(SelfUpdateArgs { + target_version, + token, + }), + }) => commands::self_update(target_version, token, printer).await, Commands::Version { output_format } => { commands::version(output_format, &mut stdout())?; Ok(ExitStatus::Success)