docs: 重构 README 新手使用说明,添加一键安装脚本文档

This commit is contained in:
2026-06-22 15:42:44 +08:00
parent cba7f9fb55
commit 04ef9b65a5
4 changed files with 529 additions and 70 deletions

View File

@@ -33,6 +33,90 @@ param (
[Parameter(HelpMessage = "Print Help")]
[switch]$Help
)
function WebProxyFromUrl {
param([string]$ProxyUrl)
if ([string]::IsNullOrWhiteSpace($ProxyUrl)) {
return $null
}
try {
# Parse the proxy URL
$uri = [System.Uri]$ProxyUrl
# Create WebProxy instance
$webProxy = New-Object System.Net.WebProxy($uri)
# Set credentials if provided in URL
if (-not [string]::IsNullOrEmpty($uri.UserInfo)) {
$userInfo = $uri.UserInfo.Split(':')
$username = [System.Uri]::UnescapeDataString($userInfo[0])
$password = if ($null -eq $userInfo[1]) { "" } else { [System.Uri]::UnescapeDataString($userInfo[1]) }
$webProxy.Credentials = New-Object System.Net.NetworkCredential($username, $password)
}
return $webProxy
}
catch {
Write-Verbose("Failed to parse proxy URL '$ProxyUrl': $($_.Exception.Message)")
return $null
}
}
function WebProxyFromEnvironment {
$httpsProxy = [System.Environment]::GetEnvironmentVariable("HTTPS_PROXY")
$allProxy = [System.Environment]::GetEnvironmentVariable("ALL_PROXY")
$proxyUrl = if (-not [string]::IsNullOrWhiteSpace($httpsProxy)) { $httpsProxy } else { $allProxy }
$webProxy = WebProxyFromUrl -ProxyUrl $proxyUrl
return $webProxy
}
# Downloads a URL to a local file using HttpClient with:
# * a configurable per-request timeout (WebClient has none)
# * proxy support from HTTPS_PROXY / ALL_PROXY
# * bearer auth, attached ONLY when the target host is a trusted first-party
# (github.com / astral.sh). This prevents leaking UV_GITHUB_TOKEN to
# third-party mirrors configured in $ArtifactDownloadUrls.
function Invoke-HttpDownload {
param(
[Parameter(Mandatory = $true)][string]$Url,
[Parameter(Mandatory = $true)][string]$Destination,
[int]$TimeoutSec = 30,
[switch]$IncludeAuth
)
$handler = New-Object System.Net.Http.HttpClientHandler
$proxy = WebProxyFromEnvironment
if ($null -ne $proxy) {
$handler.Proxy = $proxy
$handler.UseProxy = $true
}
$client = New-Object System.Net.Http.HttpClient($handler)
$client.Timeout = [TimeSpan]::FromSeconds($TimeoutSec)
$client.DefaultRequestHeaders.Add("User-Agent", "PowerShell/uv-installer")
if ($IncludeAuth -and $auth_token -and ($Url -match 'github\.com|astral\.sh')) {
$client.DefaultRequestHeaders.Add("Authorization", "Bearer $auth_token")
}
try {
$response = $client.GetAsync($Url, [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead).Result
if (-not $response.IsSuccessStatusCode) {
throw "HTTP $([int]$response.StatusCode) downloading $Url"
}
$stream = $response.Content.ReadAsStreamAsync().Result
$fileStream = [System.IO.File]::Create($Destination)
try {
$stream.CopyTo($fileStream)
} finally {
$fileStream.Dispose()
}
} finally {
$client.Dispose()
}
}
function Get-LatestVersion {
if ($env:UV_INSTALLER_VERSION) {
return $env:UV_INSTALLER_VERSION
@@ -41,10 +125,22 @@ function Get-LatestVersion {
$fallback_version = "0.11.20"
try {
$http = New-Object System.Net.Http.HttpClient
$http.Timeout = [TimeSpan]::FromSeconds(5)
$handler = New-Object System.Net.Http.HttpClientHandler
$proxy = WebProxyFromEnvironment
if ($null -ne $proxy) {
$handler.Proxy = $proxy
$handler.UseProxy = $true
}
$http = New-Object System.Net.Http.HttpClient($handler)
# Bumped from 5s -> 10s. The API call is small but in proxy/restricted
# networks 5s was almost always too tight and forced the fallback version.
$http.Timeout = [TimeSpan]::FromSeconds(10)
$http.DefaultRequestHeaders.Add("User-Agent", "PowerShell/uv-installer")
$http.DefaultRequestHeaders.Add("Accept", "application/vnd.github+json")
if ($auth_token) {
$http.DefaultRequestHeaders.Add("Authorization", "Bearer $auth_token")
}
$response = $http.GetStringAsync("https://api.github.com/repos/astral-sh/uv/releases/latest").Result
$json = $response | ConvertFrom-Json
$version = $json.tag_name
@@ -60,6 +156,9 @@ function Get-LatestVersion {
}
$app_name = 'uv'
# NOTE: $auth_token must be defined *before* Get-LatestVersion / Invoke-HttpDownload
# are invoked, since both reference it.
$auth_token = $env:UV_GITHUB_TOKEN
$app_version = Get-LatestVersion
if ($env:UV_DOWNLOAD_URL) {
$ArtifactDownloadUrls = @($env:UV_DOWNLOAD_URL)
@@ -72,6 +171,10 @@ if ($env:UV_DOWNLOAD_URL) {
$installer_base_url = $env:UV_INSTALLER_GITHUB_BASE_URL
$ArtifactDownloadUrls = @("$installer_base_url/astral-sh/uv/releases/download/$app_version")
} else {
# Default download sources, ordered by priority.
# Mirror entries exist to work around GitHub connectivity issues in mainland China.
# If you are outside China or these mirrors become unavailable, you can safely
# delete the two mirror lines and rely on the official URLs (first + last).
$ArtifactDownloadUrls = @(
"https://releases.astral.sh/github/uv/releases/download/$app_version",
"https://mirror.ghproxy.com/https://github.com/astral-sh/uv/releases/download/$app_version",
@@ -80,8 +183,6 @@ if ($env:UV_DOWNLOAD_URL) {
)
}
$auth_token = $env:UV_GITHUB_TOKEN
$receipt = @"
{"binaries":["CARGO_DIST_BINS"],"binary_aliases":{},"cdylibs":["CARGO_DIST_DYLIBS"],"cstaticlibs":["CARGO_DIST_STATICLIBS"],"install_layout":"unspecified","install_prefix":"AXO_INSTALL_PREFIX","modify_path":true,"provider":{"source":"cargo-dist","version":"0.31.0"},"source":{"app_name":"uv","name":"uv","owner":"astral-sh","release_type":"github"},"version":"$app_version"}
"@
@@ -269,44 +370,6 @@ function Get-Arch() {
}
}
function WebProxyFromUrl {
param([string]$ProxyUrl)
if ([string]::IsNullOrWhiteSpace($ProxyUrl)) {
return $null
}
try {
# Parse the proxy URL
$uri = [System.Uri]$ProxyUrl
# Create WebProxy instance
$webProxy = New-Object System.Net.WebProxy($uri)
# Set credentials if provided in URL
if (-not [string]::IsNullOrEmpty($uri.UserInfo)) {
$userInfo = $uri.UserInfo.Split(':')
$username = [System.Uri]::UnescapeDataString($userInfo[0])
$password = if ($null -eq $userInfo[1]) { "" } else { [System.Uri]::UnescapeDataString($userInfo[1]) }
$webProxy.Credentials = New-Object System.Net.NetworkCredential($username, $password)
}
return $webProxy
}
catch {
Write-Verbose("Failed to parse proxy URL '$ProxyUrl': $($_.Exception.Message)")
return $null
}
}
function WebProxyFromEnvironment {
$httpsProxy = [System.Environment]::GetEnvironmentVariable("HTTPS_PROXY")
$allProxy = [System.Environment]::GetEnvironmentVariable("ALL_PROXY")
$proxyUrl = if (-not [string]::IsNullOrWhiteSpace($httpsProxy)) { $httpsProxy } else { $allProxy }
$webProxy = WebProxyFromUrl -ProxyUrl $proxyUrl
return $webProxy
}
function Download($download_url, $platforms, $arch) {
# Lookup what we expect this platform to look like
$info = $platforms[$arch]
@@ -324,15 +387,7 @@ function Download($download_url, $platforms, $arch) {
$url = "$download_url/$artifact_name"
Write-Verbose " from $url"
Write-Verbose " to $dir_path"
$wc = New-Object Net.Webclient
$proxy = WebProxyFromEnvironment
if ($null -ne $proxy) {
$wc.Proxy = $proxy
}
if ($auth_token) {
$wc.Headers["Authorization"] = "Bearer $auth_token"
}
$wc.downloadFile($url, $dir_path)
Invoke-HttpDownload -Url $url -Destination $dir_path -TimeoutSec 30 -IncludeAuth
Write-Verbose "Unpacking to $tmp"
@@ -377,7 +432,7 @@ function Download($download_url, $platforms, $arch) {
$updater_url = "$download_url/$updater_id"
$out_name = "$tmp\uv-update.exe"
$wc.downloadFile($updater_url, $out_name)
Invoke-HttpDownload -Url $updater_url -Destination $out_name -TimeoutSec 30 -IncludeAuth
$bin_paths += $out_name
}
@@ -549,7 +604,7 @@ function Invoke-Installer($artifacts, $platforms) {
# .NET's APIs which actually do what you tell them (also apparently utf8NoBOM is the
# default in newer .NETs but I'd rather not rely on that at this point).
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[IO.File]::WriteAllLines("$receipt_home/uv-receipt.json", "$receipt", $Utf8NoBomEncoding)
[IO.File]::WriteAllLines("$receipt_home\uv-receipt.json", "$receipt", $Utf8NoBomEncoding)
}
# Respect the environment, but CLI takes precedence