-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.ps1
More file actions
144 lines (131 loc) · 5.56 KB
/
install.ps1
File metadata and controls
144 lines (131 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#Requires -Version 5.1
<#
.SYNOPSIS
Bootstrap for managed-python on Windows.
.DESCRIPTION
Downloads uv, creates a Python venv, then hands off to setup.py.
All configuration (env.ps1, env.sh, bin\ wrappers, shell profile) is
handled by setup.py.
.EXAMPLE
.\install.ps1 -Prefix "$env:USERPROFILE\.local\redmatter\python" -Python "3.10" -EnvPrefix "REDMATTER"
.EXAMPLE
.\install.ps1 -Prefix "$env:USERPROFILE\.local\redmatter\python" `
-Python "3.10" -UvEnv "REDMATTER_UV" -UvxEnv "REDMATTER_UVX" -PythonEnv "REDMATTER_PYTHON"
#>
param(
[Parameter(Mandatory=$true)] [string]$Prefix,
[Parameter(Mandatory=$true)] [string]$Python,
[Parameter(Mandatory=$false)] [string]$EnvPrefix,
[Parameter(Mandatory=$false)] [string]$UvEnv,
[Parameter(Mandatory=$false)] [string]$UvxEnv,
[Parameter(Mandatory=$false)] [string]$PythonEnv,
[switch]$ShellProfile,
[switch]$Quiet,
[switch]$Isolated
)
if ($EnvPrefix) {
if ($UvEnv -or $UvxEnv -or $PythonEnv) {
Write-Error "-EnvPrefix cannot be combined with -UvEnv, -UvxEnv, or -PythonEnv"
exit 1
}
} else {
$missing = @()
if (-not $UvEnv) { $missing += "-UvEnv" }
if (-not $UvxEnv) { $missing += "-UvxEnv" }
if (-not $PythonEnv) { $missing += "-PythonEnv" }
if ($missing.Count -gt 0) {
Write-Error "The following parameters are required: $($missing -join ', ') (or use -EnvPrefix)"
exit 1
}
}
function Write-Msg($msg) { if (-not $Quiet) { Write-Host $msg } }
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$Prefix = [Environment]::ExpandEnvironmentVariables($Prefix)
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$UvExe = Join-Path $Prefix "uv.exe"
$VenvPy = Join-Path $Prefix "venv\Scripts\python.exe"
# Read pinned uv version from distro.toml
$UvVersion = (Get-Content (Join-Path $ScriptDir "distro.toml") |
Select-String '^uv_version').Line `
-replace '^[^=]+=\s*"?([^"#]+)"?.*', '$1' |
ForEach-Object { $_.Trim() }
Write-Msg ""
Write-Msg "managed-python bootstrap"
Write-Msg " prefix $Prefix"
Write-Msg ""
# Bootstrap uv
$currentVer = if (Test-Path $UvExe) { try { (& $UvExe --version 2>$null) -split " " | Select-Object -Last 1 } catch { "" } } else { "" }
if ($currentVer -eq $UvVersion) {
Write-Msg " ✓ uv $UvVersion"
} else {
Write-Msg " → Downloading uv $UvVersion"
$arch = if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { "aarch64" } else { "x86_64" }
$url = "https://github.com/astral-sh/uv/releases/download/$UvVersion/uv-${arch}-pc-windows-msvc.zip"
New-Item -ItemType Directory -Force -Path $Prefix | Out-Null
$tmp = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::ChangeExtension([IO.Path]::GetRandomFileName(), ".zip"))
$tmpDir = "$tmp.dir"
$tomlMatch = Get-Content (Join-Path $ScriptDir "distro.toml") |
Select-String "^${arch}-pc-windows-msvc\s*=\s*`"([^`"]+)`""
$expectedHash = $tomlMatch.Matches[0].Groups[1].Value.ToUpper()
if (-not $expectedHash) {
Write-Error "No pinned checksum for ${arch}-pc-windows-msvc in distro.toml"
exit 1
}
try {
$ProgressPreference = "SilentlyContinue"
Invoke-WebRequest $url -OutFile $tmp -UseBasicParsing
$actualHash = (Get-FileHash $tmp -Algorithm SHA256).Hash.ToUpper()
if ($actualHash -ne $expectedHash) {
Write-Error "uv $UvVersion checksum verification failed — download may be corrupt or tampered`n expected: $expectedHash`n actual: $actualHash"
exit 1
}
Expand-Archive $tmp $tmpDir -Force
$uvSrc = Get-ChildItem $tmpDir -Filter "uv.exe" -Recurse | Select-Object -First 1
Copy-Item $uvSrc.FullName $UvExe -Force
$uvxSrc = Get-ChildItem $tmpDir -Filter "uvx.exe" -Recurse | Select-Object -First 1
if (-not $uvxSrc) { Write-Error "Failed to locate uvx.exe in archive"; exit 1 }
Copy-Item $uvxSrc.FullName (Join-Path $Prefix "uvx.exe") -Force
} catch {
Write-Error "Failed to download uv $UvVersion from $url`: $_"
exit 1
} finally {
Remove-Item $tmp, $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
}
if (-not (Test-Path $UvExe)) {
Write-Error "Failed to download uv $UvVersion — binary not found after extraction"
exit 1
}
Write-Msg " ✓ uv $UvVersion installed"
}
# Bootstrap venv
if (Test-Path $VenvPy) {
Write-Msg " ✓ venv already exists"
} else {
Write-Msg " → Creating Python $Python venv"
$pythonPref = if ($Isolated) { "only-managed" } else { "system" }
$venvArgs = @("venv", "--python", $Python, "--python-preference", $pythonPref)
if ($Quiet) { $venvArgs += "--quiet" }
& $UvExe @venvArgs (Join-Path $Prefix "venv")
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to create Python $Python venv — see uv error above"
exit $LASTEXITCODE
}
if (-not (Test-Path $VenvPy)) {
Write-Error "Failed to create Python $Python venv — python.exe not found at $VenvPy"
exit 1
}
Write-Msg " ✓ venv created"
}
Write-Msg ""
# Hand off to setup.py
if ($EnvPrefix) {
$setupArgs = @("--prefix", $Prefix, "--python", $Python, "--env-prefix", $EnvPrefix)
} else {
$setupArgs = @("--prefix", $Prefix, "--python", $Python, "--uv-env", $UvEnv, "--uvx-env", $UvxEnv, "--python-env", $PythonEnv)
}
if ($ShellProfile) { $setupArgs += "--shell-profile" }
if ($Isolated) { $setupArgs += "--isolated" }
if ($Quiet) { $setupArgs += "--quiet" }
& $VenvPy (Join-Path $ScriptDir "setup.py") @setupArgs
exit $LASTEXITCODE