mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-04-20 01:12:16 +00:00
Merge branch 'main' into arm64-build-support
This commit is contained in:
252
misc/tools.func
252
misc/tools.func
@@ -969,13 +969,43 @@ verify_repo_available() {
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Ensure dependencies are installed (with apt update caching)
|
||||
# Ensure dependencies are installed (with apt/apk update caching)
|
||||
# Supports both Debian (apt/dpkg) and Alpine (apk) systems
|
||||
# ------------------------------------------------------------------------------
|
||||
ensure_dependencies() {
|
||||
local deps=("$@")
|
||||
local missing=()
|
||||
|
||||
# Fast batch check using dpkg-query (much faster than individual checks)
|
||||
# Detect Alpine Linux
|
||||
if [[ -f /etc/alpine-release ]]; then
|
||||
for dep in "${deps[@]}"; do
|
||||
if command -v "$dep" &>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
if apk info -e "$dep" &>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
missing+=("$dep")
|
||||
done
|
||||
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
$STD apk add --no-cache "${missing[@]}" || {
|
||||
local failed=()
|
||||
for pkg in "${missing[@]}"; do
|
||||
if ! $STD apk add --no-cache "$pkg" 2>/dev/null; then
|
||||
failed+=("$pkg")
|
||||
fi
|
||||
done
|
||||
if [[ ${#failed[@]} -gt 0 ]]; then
|
||||
msg_error "Failed to install dependencies: ${failed[*]}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Debian/Ubuntu: Fast batch check using dpkg-query
|
||||
local installed_pkgs
|
||||
installed_pkgs=$(dpkg-query -W -f='${Package}\n' 2>/dev/null | sort -u)
|
||||
|
||||
@@ -1072,11 +1102,53 @@ create_temp_dir() {
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Check if package is installed (faster than dpkg -l | grep)
|
||||
# Check if package is installed (supports both Debian and Alpine)
|
||||
# ------------------------------------------------------------------------------
|
||||
is_package_installed() {
|
||||
local package="$1"
|
||||
dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$"
|
||||
if [[ -f /etc/alpine-release ]]; then
|
||||
apk info -e "$package" &>/dev/null
|
||||
else
|
||||
dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Prompt user to enter a GitHub Personal Access Token (PAT) interactively
|
||||
# Returns 0 if a valid token was provided, 1 otherwise
|
||||
# ------------------------------------------------------------------------------
|
||||
prompt_for_github_token() {
|
||||
if [[ ! -t 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local reply
|
||||
read -rp "${TAB}Would you like to enter a GitHub Personal Access Token (PAT)? [y/N]: " reply
|
||||
reply="${reply:-n}"
|
||||
|
||||
if [[ ! "${reply,,}" =~ ^(y|yes)$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local token
|
||||
while true; do
|
||||
read -rp "${TAB}Enter your GitHub PAT: " token
|
||||
# Trim leading/trailing whitespace
|
||||
token="$(echo "$token" | xargs)"
|
||||
if [[ -z "$token" ]]; then
|
||||
msg_warn "Token cannot be empty. Please try again."
|
||||
continue
|
||||
fi
|
||||
if [[ "$token" =~ [[:space:]] ]]; then
|
||||
msg_warn "Token must not contain spaces. Please try again."
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
|
||||
export GITHUB_TOKEN="$token"
|
||||
msg_ok "GitHub token has been set."
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -1091,7 +1163,8 @@ github_api_call() {
|
||||
local header_args=()
|
||||
[[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN")
|
||||
|
||||
for attempt in $(seq 1 $max_retries); do
|
||||
local attempt=1
|
||||
while ((attempt <= max_retries)); do
|
||||
local http_code
|
||||
http_code=$(curl -sSL -w "%{http_code}" -o "$output_file" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
@@ -1108,7 +1181,11 @@ github_api_call() {
|
||||
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
||||
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
|
||||
else
|
||||
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
|
||||
msg_error "The repository may require authentication."
|
||||
fi
|
||||
if prompt_for_github_token; then
|
||||
header_args=(-H "Authorization: Bearer $GITHUB_TOKEN")
|
||||
continue
|
||||
fi
|
||||
return 1
|
||||
;;
|
||||
@@ -1118,9 +1195,16 @@ github_api_call() {
|
||||
msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)"
|
||||
sleep "$retry_delay"
|
||||
retry_delay=$((retry_delay * 2))
|
||||
((attempt++))
|
||||
continue
|
||||
fi
|
||||
msg_error "GitHub API rate limit exceeded (HTTP 403)."
|
||||
if prompt_for_github_token; then
|
||||
header_args=(-H "Authorization: Bearer $GITHUB_TOKEN")
|
||||
retry_delay=2
|
||||
attempt=1
|
||||
continue
|
||||
fi
|
||||
msg_error "To increase the limit, export a GitHub token before running the script:"
|
||||
msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\""
|
||||
return 1
|
||||
@@ -1132,6 +1216,7 @@ github_api_call() {
|
||||
000 | "")
|
||||
if [[ $attempt -lt $max_retries ]]; then
|
||||
sleep "$retry_delay"
|
||||
((attempt++))
|
||||
continue
|
||||
fi
|
||||
msg_error "GitHub API connection failed (no response)."
|
||||
@@ -1141,12 +1226,14 @@ github_api_call() {
|
||||
*)
|
||||
if [[ $attempt -lt $max_retries ]]; then
|
||||
sleep "$retry_delay"
|
||||
((attempt++))
|
||||
continue
|
||||
fi
|
||||
msg_error "GitHub API call failed (HTTP $http_code)."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
msg_error "GitHub API call failed after ${max_retries} attempts: ${url}"
|
||||
@@ -3130,11 +3217,30 @@ function fetch_and_deploy_gh_release() {
|
||||
if [[ "$http_code" == "200" ]]; then
|
||||
success=true
|
||||
break
|
||||
elif [[ "$http_code" == "401" ]]; then
|
||||
msg_error "GitHub API authentication failed (HTTP 401)."
|
||||
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
||||
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
|
||||
else
|
||||
msg_error "The repository may require authentication."
|
||||
fi
|
||||
if prompt_for_github_token; then
|
||||
header=(-H "Authorization: token $GITHUB_TOKEN")
|
||||
continue
|
||||
fi
|
||||
break
|
||||
elif [[ "$http_code" == "403" ]]; then
|
||||
if ((attempt < max_retries)); then
|
||||
msg_warn "GitHub API rate limit hit, retrying in ${retry_delay}s... (attempt $attempt/$max_retries)"
|
||||
sleep "$retry_delay"
|
||||
retry_delay=$((retry_delay * 2))
|
||||
else
|
||||
msg_error "GitHub API rate limit exceeded (HTTP 403)."
|
||||
if prompt_for_github_token; then
|
||||
header=(-H "Authorization: token $GITHUB_TOKEN")
|
||||
retry_delay=2
|
||||
attempt=0
|
||||
fi
|
||||
fi
|
||||
else
|
||||
sleep "$retry_delay"
|
||||
@@ -3143,21 +3249,10 @@ function fetch_and_deploy_gh_release() {
|
||||
done
|
||||
|
||||
if ! $success; then
|
||||
if [[ "$http_code" == "401" ]]; then
|
||||
msg_error "GitHub API authentication failed (HTTP 401)."
|
||||
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
||||
msg_error "Your GITHUB_TOKEN appears to be invalid or expired."
|
||||
else
|
||||
msg_error "The repository may require authentication. Try: export GITHUB_TOKEN=\"ghp_your_token\""
|
||||
fi
|
||||
elif [[ "$http_code" == "403" ]]; then
|
||||
msg_error "GitHub API rate limit exceeded (HTTP 403)."
|
||||
msg_error "To increase the limit, export a GitHub token before running the script:"
|
||||
msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\""
|
||||
elif [[ "$http_code" == "000" || -z "$http_code" ]]; then
|
||||
if [[ "$http_code" == "000" || -z "$http_code" ]]; then
|
||||
msg_error "GitHub API connection failed (no response)."
|
||||
msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit"
|
||||
else
|
||||
elif [[ "$http_code" != "401" ]]; then
|
||||
msg_error "Failed to fetch release metadata (HTTP $http_code)"
|
||||
fi
|
||||
return 1
|
||||
@@ -4474,9 +4569,8 @@ _setup_amd_gpu() {
|
||||
fi
|
||||
# Ubuntu includes AMD firmware in linux-firmware by default
|
||||
|
||||
# ROCm for compute (optional - large download)
|
||||
# Uncomment if needed:
|
||||
# $STD apt -y install rocm-opencl-runtime 2>/dev/null || true
|
||||
# ROCm compute stack (OpenCL + HIP)
|
||||
_setup_rocm "$os_id" "$os_codename"
|
||||
|
||||
msg_ok "AMD GPU configured"
|
||||
}
|
||||
@@ -4501,9 +4595,103 @@ _setup_amd_apu() {
|
||||
$STD apt -y install firmware-amd-graphics 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ROCm compute stack (OpenCL + HIP) - also works for many APUs
|
||||
_setup_rocm "$os_id" "$os_codename"
|
||||
|
||||
msg_ok "AMD APU configured"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# AMD ROCm Compute Setup
|
||||
# Adds ROCm repository and installs the ROCm compute stack for AMD GPUs/APUs.
|
||||
# Provides: OpenCL, HIP, rocm-smi, rocminfo
|
||||
# Supported: Debian 12/13, Ubuntu 22.04/24.04 (amd64 only)
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
_setup_rocm() {
|
||||
local os_id="$1" os_codename="$2"
|
||||
|
||||
# Only amd64 is supported
|
||||
if [[ "$(dpkg --print-architecture 2>/dev/null)" != "amd64" ]]; then
|
||||
msg_warn "ROCm is only available for amd64 — skipping"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local ROCM_VERSION="7.2"
|
||||
local ROCM_REPO_CODENAME
|
||||
|
||||
# Map OS codename to ROCm repository codename (Ubuntu-based repos)
|
||||
case "${os_id}-${os_codename}" in
|
||||
debian-bookworm) ROCM_REPO_CODENAME="jammy" ;;
|
||||
debian-trixie | debian-sid) ROCM_REPO_CODENAME="noble" ;;
|
||||
ubuntu-jammy) ROCM_REPO_CODENAME="jammy" ;;
|
||||
ubuntu-noble) ROCM_REPO_CODENAME="noble" ;;
|
||||
*)
|
||||
msg_warn "ROCm not supported on ${os_id} ${os_codename} — skipping"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
msg_info "Installing ROCm ${ROCM_VERSION} compute stack"
|
||||
|
||||
# ROCm main repository (userspace compute libs)
|
||||
setup_deb822_repo \
|
||||
"rocm" \
|
||||
"https://repo.radeon.com/rocm/rocm.gpg.key" \
|
||||
"https://repo.radeon.com/rocm/apt/${ROCM_VERSION}" \
|
||||
"${ROCM_REPO_CODENAME}" \
|
||||
"main" \
|
||||
"amd64" || {
|
||||
msg_warn "Failed to add ROCm repository — skipping ROCm"
|
||||
return 0
|
||||
}
|
||||
|
||||
# AMDGPU driver repository (append to same keyring)
|
||||
{
|
||||
echo ""
|
||||
echo "Types: deb"
|
||||
echo "URIs: https://repo.radeon.com/amdgpu/latest/ubuntu"
|
||||
echo "Suites: ${ROCM_REPO_CODENAME}"
|
||||
echo "Components: main"
|
||||
echo "Architectures: amd64"
|
||||
echo "Signed-By: /etc/apt/keyrings/rocm.gpg"
|
||||
} >>/etc/apt/sources.list.d/rocm.sources
|
||||
|
||||
# Pin ROCm packages to prefer radeon repo
|
||||
cat <<EOF >/etc/apt/preferences.d/rocm-pin-600
|
||||
Package: *
|
||||
Pin: release o=repo.radeon.com
|
||||
Pin-Priority: 600
|
||||
EOF
|
||||
|
||||
$STD apt update
|
||||
# Install only runtime packages — full 'rocm' meta-package includes 15GB+ dev tools
|
||||
$STD apt install -y rocm-opencl-runtime rocm-hip-runtime rocm-smi-lib 2>/dev/null || {
|
||||
msg_warn "ROCm runtime install failed — trying minimal set"
|
||||
$STD apt install -y rocm-opencl-runtime rocm-smi-lib 2>/dev/null || msg_warn "ROCm minimal install also failed"
|
||||
}
|
||||
|
||||
# Group membership for GPU access
|
||||
usermod -aG render,video root 2>/dev/null || true
|
||||
|
||||
# Environment (PATH + LD_LIBRARY_PATH)
|
||||
if [[ -d /opt/rocm ]]; then
|
||||
cat <<'ENVEOF' >/etc/profile.d/rocm.sh
|
||||
export PATH="\$PATH:/opt/rocm/bin"
|
||||
export LD_LIBRARY_PATH="\${LD_LIBRARY_PATH:+\$LD_LIBRARY_PATH:}/opt/rocm/lib"
|
||||
ENVEOF
|
||||
chmod +x /etc/profile.d/rocm.sh
|
||||
# Also make available for current session / systemd services
|
||||
echo "/opt/rocm/lib" >/etc/ld.so.conf.d/rocm.conf
|
||||
ldconfig 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ -x /opt/rocm/bin/rocminfo ]]; then
|
||||
msg_ok "ROCm ${ROCM_VERSION} installed"
|
||||
else
|
||||
msg_warn "ROCm installed but rocminfo not found — GPU may not be available in container"
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# NVIDIA GPU Setup
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -8159,3 +8347,23 @@ function fetch_and_deploy_from_url() {
|
||||
msg_ok "Successfully deployed archive to $directory"
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_nonfree() {
|
||||
local sources_file="/etc/apt/sources.list.d/debian-nonfree.sources"
|
||||
|
||||
if [ ! -f "$sources_file" ]; then
|
||||
cat <<EOF >$sources_file
|
||||
Types: deb
|
||||
URIs: http://deb.debian.org/debian
|
||||
Suites: trixie trixie-updates
|
||||
Components: main contrib non-free non-free-firmware
|
||||
|
||||
Types: deb
|
||||
URIs: http://security.debian.org/debian-security
|
||||
Suites: trixie-security
|
||||
Components: main contrib non-free non-free-firmware
|
||||
EOF
|
||||
fi
|
||||
$STD apt update
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user