mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Add a uv self update command (#2228)
## Summary Powered by Axo: https://github.com/axodotdev/axoupdater. Closes https://github.com/astral-sh/uv/issues/1591. ## Test Plan To test locally: - `rm -f ~/.config/uv/uv-receipt.json /Users/crmarsh/.cargo/bin/uv` - `curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.1.14/uv-installer.sh | sh` - `cargo run self update` Up-to-date:  Updated:  No receipt: 
This commit is contained in:
parent
c4107f9c40
commit
1bf48c91f2
6 changed files with 331 additions and 14 deletions
198
Cargo.lock
generated
198
Cargo.lock
generated
|
|
@ -275,6 +275,52 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axoasset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dce2f189800bafe8322ef3a4d361ee7373bfc2f8fe052afda404230166dc45f"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"image",
|
||||
"miette 7.2.0",
|
||||
"mime",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"url",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axoprocess"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4de46920588aef95658797996130bacd542436aee090084646521260a74bda7d"
|
||||
dependencies = [
|
||||
"miette 7.2.0",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axoupdater"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b3130c1f3911eecdb1caf0412160c62758e314b644377796eb64917539ba8c"
|
||||
dependencies = [
|
||||
"axoasset",
|
||||
"axoprocess",
|
||||
"camino",
|
||||
"homedir",
|
||||
"miette 7.2.0",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"temp-dir",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backoff"
|
||||
version = "0.4.0"
|
||||
|
|
@ -469,6 +515,15 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-util"
|
||||
version = "0.2.9"
|
||||
|
|
@ -838,7 +893,7 @@ version = "3.4.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.28.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
|
|
@ -1469,6 +1524,20 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "homedir"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22074da8bba2ef26fc1737ae6c777b5baab5524c2dc403b5c6a76166766ccda5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"nix 0.26.4",
|
||||
"serde",
|
||||
"widestring",
|
||||
"windows-sys 0.48.0",
|
||||
"wmi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html-escape"
|
||||
version = "0.2.13"
|
||||
|
|
@ -1676,6 +1745,18 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imagesize"
|
||||
version = "0.11.0"
|
||||
|
|
@ -2046,6 +2127,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
|
|
@ -2063,7 +2153,7 @@ checksum = "337e1043bbc086dac9d9674983bef52ac991ce150e09b5b8e35c5a73dd83f66c"
|
|||
dependencies = [
|
||||
"backtrace",
|
||||
"backtrace-ext",
|
||||
"miette-derive",
|
||||
"miette-derive 6.0.1",
|
||||
"owo-colors 3.5.0",
|
||||
"supports-color",
|
||||
"supports-hyperlinks",
|
||||
|
|
@ -2074,6 +2164,18 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"miette-derive 7.2.0",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "6.0.1"
|
||||
|
|
@ -2085,6 +2187,17 @@ dependencies = [
|
|||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.39"
|
||||
|
|
@ -2149,6 +2262,19 @@ dependencies = [
|
|||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.7.1",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.28.0"
|
||||
|
|
@ -2637,7 +2763,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"memoffset 0.9.0",
|
||||
"parking_lot 0.12.1",
|
||||
"portable-atomic",
|
||||
"pyo3-build-config",
|
||||
|
|
@ -2865,7 +2991,7 @@ checksum = "52b1349400e2ffd64a9fb5ed9008e33c0b8ef86bd5bae8f73080839c7082f1d5"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"windows",
|
||||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3665,6 +3791,12 @@ dependencies = [
|
|||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "temp-dir"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd16aa9ffe15fe021c6ee3766772132c6e98dfa395a167e16864f61a9cfb71d6"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
|
|
@ -3894,6 +4026,7 @@ dependencies = [
|
|||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
|
|
@ -4306,6 +4439,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"assert_cmd",
|
||||
"assert_fs",
|
||||
"axoupdater",
|
||||
"base64 0.21.7",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
|
|
@ -4323,7 +4457,7 @@ dependencies = [
|
|||
"insta",
|
||||
"install-wheel-rs",
|
||||
"itertools 0.12.1",
|
||||
"miette",
|
||||
"miette 6.0.1",
|
||||
"mimalloc",
|
||||
"owo-colors 4.0.0",
|
||||
"pep508_rs",
|
||||
|
|
@ -5026,6 +5160,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
@ -5057,6 +5197,18 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core 0.52.0",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
|
|
@ -5086,6 +5238,28 @@ dependencies = [
|
|||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.0"
|
||||
|
|
@ -5270,6 +5444,20 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wmi"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f0a4062ca522aad4705a2948fd4061b3857537990202a8ddd5af21607f79a"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@ license = "MIT OR Apache-2.0"
|
|||
[workspace.dependencies]
|
||||
anstream = { version = "0.6.13" }
|
||||
anyhow = { version = "1.0.80" }
|
||||
async-compression = { version = "0.4.6" }
|
||||
async-channel = { version = "2.2.0" }
|
||||
async-trait = { version = "0.1.78" }
|
||||
async-compression = { version = "0.4.6" }
|
||||
async-recursion = { version = "1.0.5" }
|
||||
async-trait = { version = "0.1.78" }
|
||||
async_http_range_reader = { version = "0.7.0" }
|
||||
async_zip = { git = "https://github.com/charliermarsh/rs-async-zip", rev = "d76801da0943de985254fc6255c0e476b57c5836", features = ["deflate"] }
|
||||
axoupdater = { version = "0.3.1", default-features = false }
|
||||
backoff = { version = "0.4.0" }
|
||||
base64 = { version = "0.21.7" }
|
||||
cachedir = { version = "0.3.1" }
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ uv-warnings = { path = "../uv-warnings" }
|
|||
|
||||
anstream = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
axoupdater = { workspace = true, features = ["github_releases", "tokio"] }
|
||||
base64 = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "string"] }
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ pub(crate) use pip_list::pip_list;
|
|||
pub(crate) use pip_show::pip_show;
|
||||
pub(crate) use pip_sync::pip_sync;
|
||||
pub(crate) use pip_uninstall::pip_uninstall;
|
||||
pub(crate) use self_update::self_update;
|
||||
use uv_cache::Cache;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::compile_tree;
|
||||
|
|
@ -36,6 +37,7 @@ mod pip_show;
|
|||
mod pip_sync;
|
||||
mod pip_uninstall;
|
||||
mod reporters;
|
||||
mod self_update;
|
||||
mod venv;
|
||||
mod version;
|
||||
|
||||
|
|
|
|||
114
crates/uv/src/commands/self_update.rs
Normal file
114
crates/uv/src/commands/self_update.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use axoupdater::{AxoUpdater, AxoupdateError};
|
||||
use owo_colors::OwoColorize;
|
||||
use tracing::debug;
|
||||
use uv_client::BetterReqwestError;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::printer::Printer;
|
||||
|
||||
/// Attempt to update the `uv` binary.
|
||||
pub(crate) async fn self_update(printer: Printer) -> Result<ExitStatus> {
|
||||
let mut updater = AxoUpdater::new_for("uv");
|
||||
updater.disable_installer_output();
|
||||
|
||||
// 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 {
|
||||
debug!("no receipt found; assuming `uv` was installed via a package manager");
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
format_args!(
|
||||
concat!(
|
||||
"{}{} Self-update is only available for `uv` binaries installed via the standalone installation scripts.",
|
||||
"\n",
|
||||
"\n",
|
||||
"If you installed `uv` with `pip`, `brew`, or another package manager, update `uv` with `pip install --upgrade`, `brew upgrade`, or similar."
|
||||
),
|
||||
"warning".yellow().bold(),
|
||||
":".bold()
|
||||
)
|
||||
)?;
|
||||
return Ok(ExitStatus::Error);
|
||||
};
|
||||
|
||||
// Ensure the receipt is for the current binary. If it's not, then the user likely has multiple
|
||||
// `uv` binaries installed, and the current binary was _not_ installed via the standalone
|
||||
// installation scripts.
|
||||
if !updater.check_receipt_is_for_this_executable()? {
|
||||
debug!(
|
||||
"receipt is not for this executable; assuming `uv` was installed via a package manager"
|
||||
);
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
format_args!(
|
||||
concat!(
|
||||
"{}{} Self-update is only available for `uv` binaries installed via the standalone installation scripts.",
|
||||
"\n",
|
||||
"\n",
|
||||
"If you installed `uv` with `pip`, `brew`, or another package manager, update `uv` with `pip install --upgrade`, `brew upgrade`, or similar."
|
||||
),
|
||||
"warning".yellow().bold(),
|
||||
":".bold()
|
||||
)
|
||||
)?;
|
||||
return Ok(ExitStatus::Error);
|
||||
}
|
||||
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
format_args!(
|
||||
"{}{} Checking for updates...",
|
||||
"info".cyan().bold(),
|
||||
":".bold()
|
||||
)
|
||||
)?;
|
||||
|
||||
// Run the updater. This involves a network request, since we need to determine the latest
|
||||
// available version of `uv`.
|
||||
match updater.run().await {
|
||||
Ok(Some(result)) => {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
format_args!(
|
||||
"{}{} Upgraded `uv` to {}! {}",
|
||||
"success".green().bold(),
|
||||
":".bold(),
|
||||
format!("v{}", result.new_version).bold().white(),
|
||||
format!(
|
||||
"https://github.com/astral-sh/uv/releases/tag/{}",
|
||||
result.new_version_tag
|
||||
)
|
||||
.cyan()
|
||||
)
|
||||
)?;
|
||||
}
|
||||
Ok(None) => {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
format_args!(
|
||||
"{}{} You're on the latest version of `uv` ({}).",
|
||||
"success".green().bold(),
|
||||
":".bold(),
|
||||
format!("v{}", env!("CARGO_PKG_VERSION")).bold().white()
|
||||
)
|
||||
)?;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(if let AxoupdateError::Reqwest(err) = err {
|
||||
BetterReqwestError::from(err).into()
|
||||
} else {
|
||||
err.into()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
|
@ -137,6 +137,9 @@ enum Commands {
|
|||
Venv(VenvArgs),
|
||||
/// Manage the cache.
|
||||
Cache(CacheNamespace),
|
||||
/// Manage the `uv` executable.
|
||||
#[clap(name = "self")]
|
||||
Self_(SelfNamespace),
|
||||
/// Remove all items from the cache.
|
||||
#[clap(hide = true)]
|
||||
Clean(CleanArgs),
|
||||
|
|
@ -150,6 +153,18 @@ enum Commands {
|
|||
GenerateShellCompletion { shell: clap_complete_command::Shell },
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct SelfNamespace {
|
||||
#[clap(subcommand)]
|
||||
command: SelfCommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum SelfCommand {
|
||||
/// Update `uv` to the latest version.
|
||||
Update,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct CacheNamespace {
|
||||
#[clap(subcommand)]
|
||||
|
|
@ -171,13 +186,6 @@ struct CleanArgs {
|
|||
package: Vec<PackageName>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct DirArgs {
|
||||
/// The packages to remove from the cache.
|
||||
package: Vec<PackageName>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct PipNamespace {
|
||||
#[clap(subcommand)]
|
||||
|
|
@ -1794,6 +1802,9 @@ async fn run() -> Result<ExitStatus> {
|
|||
)
|
||||
.await
|
||||
}
|
||||
Commands::Self_(SelfNamespace {
|
||||
command: SelfCommand::Update,
|
||||
}) => commands::self_update(printer).await,
|
||||
Commands::Version { output_format } => {
|
||||
commands::version(output_format, &mut stdout())?;
|
||||
Ok(ExitStatus::Success)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue