-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·154 lines (133 loc) · 5.52 KB
/
install.sh
File metadata and controls
executable file
·154 lines (133 loc) · 5.52 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
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env bash
# install.sh — Bootstrap uv + Python venv, then hand off to setup.py
#
# Usage:
# ./install.sh --prefix PATH --python X.Y --uv-env NAME --python-env NAME [--isolated] [--shell-profile]
#
# Bootstrap phase (this script): download uv, create venv.
# Configuration phase (setup.py): env.sh, env.ps1, bin/ wrappers, shell profile.
_msg() { [ "${quiet:-}" != "1" ] && printf "%s\n" "$1"; return 0; }
_uv_download_url() {
local uv_version="$1" os arch target
case "$(uname -s)" in
Linux) os=linux ;;
Darwin) os=macos ;;
*) printf "Unsupported OS: %s\n" "$(uname -s)" >&2; return 1 ;;
esac
case "$(uname -m)" in
x86_64) arch=x86_64 ;;
aarch64|arm64) arch=aarch64 ;;
*) printf "Unsupported arch: %s\n" "$(uname -m)" >&2; return 1 ;;
esac
[[ "$os" == linux ]] \
&& target="${arch}-unknown-linux-gnu" \
|| target="${arch}-apple-darwin"
printf "https://github.com/astral-sh/uv/releases/download/%s/uv-%s.tar.gz" \
"$uv_version" "$target"
}
_uv_expected_hash() {
local distro_toml="$1" target="$2"
grep "^${target}" "$distro_toml" \
| sed -E 's/^[^=]+=[[:space:]]*"([^"]+)".*/\1/'
}
_bootstrap_uv() {
local prefix="$1" uv_version="$2" distro_toml="$3"
local uv_bin="${prefix}/uv"
if [[ -x "$uv_bin" ]] && \
[[ "$("$uv_bin" --version 2>/dev/null | awk '{print $2}')" == "$uv_version" ]]; then
_msg " ✓ uv $uv_version"; return
fi
_msg " → Downloading uv $uv_version"
local url tmp target
url="$(_uv_download_url "$uv_version")"
# Derive target triple from the URL for checksum lookup (strip leading "uv-")
target="$(basename "$url" .tar.gz)"
target="${target#uv-}"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"; trap - RETURN' RETURN
if command -v curl &>/dev/null; then
curl -fsSL "$url" -o "${tmp}/uv.tar.gz" \
|| { printf "ERROR: Failed to download uv %s\n" "$uv_version" >&2; exit 1; }
elif command -v wget &>/dev/null; then
wget -qO "${tmp}/uv.tar.gz" "$url" \
|| { printf "ERROR: Failed to download uv %s\n" "$uv_version" >&2; exit 1; }
else
printf "ERROR: curl or wget required\n" >&2; exit 1
fi
local expected_hash actual_hash
expected_hash="$(_uv_expected_hash "$distro_toml" "$target")"
if [[ -z "$expected_hash" ]]; then
printf "ERROR: no pinned checksum for target %s in distro.toml\n" "$target" >&2; exit 1
fi
if command -v sha256sum &>/dev/null; then
actual_hash="$(sha256sum "${tmp}/uv.tar.gz" | awk '{print $1}')"
elif command -v shasum &>/dev/null; then
actual_hash="$(shasum -a 256 "${tmp}/uv.tar.gz" | awk '{print $1}')"
else
printf "ERROR: sha256sum or shasum is required for checksum verification\n" >&2; exit 1
fi
if [[ "$actual_hash" != "$expected_hash" ]]; then
printf "ERROR: uv %s checksum verification failed\n expected: %s\n actual: %s\n" \
"$uv_version" "$expected_hash" "$actual_hash" >&2; exit 1
fi
tar -xzf "${tmp}/uv.tar.gz" -C "$tmp"
mkdir -p "$prefix"
local uv_src
uv_src="$(find "$tmp" -name "uv" -type f | head -1 || true)"
if [[ -z "$uv_src" || ! -f "$uv_src" ]]; then
printf "ERROR: failed to locate uv binary in downloaded archive\n" >&2; exit 1
fi
cp "$uv_src" "$uv_bin"
chmod +x "$uv_bin"
_msg " ✓ uv $uv_version installed"
}
_bootstrap_venv() {
local prefix="$1" min_python="$2" isolated="${3:-}"
if [[ -x "${prefix}/venv/bin/python" ]]; then
_msg " ✓ venv already exists"; return
fi
_msg " → Creating Python $min_python venv"
local pref_args="--python-preference system"
[[ "$isolated" == "1" ]] && pref_args="--python-preference only-managed"
"${prefix}/uv" venv --python "$min_python" $pref_args ${quiet:+--quiet} "${prefix}/venv" \
|| { printf "ERROR: Failed to create Python %s venv — see uv error above\n" "$min_python" >&2; exit 1; }
_msg " ✓ venv created"
}
main() {
set -euo pipefail
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local uv_version
uv_version="$(grep '^uv_version' "${script_dir}/distro.toml" \
| sed -E 's/^[^=]+=[[:space:]]*"?([^"#]+)"?.*/\1/' | tr -d '[:space:]')"
# Extract --prefix, --python, --isolated, and --quiet for bootstrap (all flags forwarded to setup.py)
local prefix="" min_python="" isolated="" quiet=""
local i j
for (( i=1; i<=$#; i++ )); do
case "${!i}" in
--prefix)
j=$((i+1))
if (( j > $# )); then printf "ERROR: --prefix requires a value\n" >&2; exit 1; fi
prefix="${!j}"; prefix="${prefix/#\~/$HOME}" ;;
--python)
j=$((i+1))
if (( j > $# )); then printf "ERROR: --python requires a value\n" >&2; exit 1; fi
min_python="${!j}" ;;
--isolated) isolated=1 ;;
--quiet|-q) quiet=1 ;;
esac
done
[[ -z "$prefix" ]] && { printf "ERROR: --prefix is required\n" >&2; exit 1; }
[[ -z "$min_python" ]] && { printf "ERROR: --python is required\n" >&2; exit 1; }
_msg ""
_msg "managed-python bootstrap"
_msg " prefix $prefix"
_msg ""
_bootstrap_uv "$prefix" "$uv_version" "${script_dir}/distro.toml"
_bootstrap_venv "$prefix" "$min_python" "$isolated"
_msg ""
exec "${prefix}/venv/bin/python" "${script_dir}/setup.py" "$@"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi