#!/usr/bin/env bash # # Summary: Install a published binary package # # Usage: pyenv binary install # # Selects a compatible package from pyenv.github.io and installs it under # . Ordinary `pyenv install' is unchanged. # set -e [ -n "$PYENV_DEBUG" ] && set -x if [ "$1" = "--complete" ]; then exit fi if [ "$#" -ne 1 ]; then pyenv-help --usage binary-install >&2 exit 1 fi case "$1" in */* | .. | . | *[[:cntrl:]]* ) echo "pyenv-binary: invalid version name \`$1'" >&2 exit 1 ;; esac requested="$1" os="$(uname -s)" arch="$(uname -m)" case "$os" in Linux ) if [ -r /etc/os-release ]; then distro="$(. /etc/os-release && printf '%s %s' "${ID:-}" "${VERSION_ID:-}")" elif type -p lsb_release >/dev/null; then distro="$(lsb_release -si) $(lsb_release -sr)" fi distro="$(printf '%s' "$distro" | tr '[:upper:]' '[:lower:]')" ;; Darwin ) distro="macos $(sw_vers -productVersion)" ;; esac tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT base_url="https://pyenv.github.io/pythons/binaries" manifest_dir="$(dirname "${BASH_SOURCE[0]}")/../share/pyenv-binary/versions" case "$requested" in *.*.* ) manifests=( "$manifest_dir/${requested%.*}" ) ;; *.* ) manifests=( "$manifest_dir/$requested" ) ;; * ) manifests=( "$manifest_dir/$requested".* ) ;; esac matches="$tmpdir/matches" : > "$matches" for manifest in "${manifests[@]}"; do [ -f "$manifest" ] || continue while IFS=$'\t' read -r source_version name target_os target_arch target_distro sha256; do case "$source_version" in "$requested" | "$requested".* ) ;; * ) continue ;; esac [[ "$source_version" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]] || continue [ "$target_os" = "$os" ] && [ "$target_arch" = "$arch" ] || continue case "$name" in "" | . | .. | *[!A-Za-z0-9._-]* ) continue ;; esac case "$os" in Linux ) [ "$(printf '%s' "$target_distro" | tr '[:upper:]' '[:lower:]')" = "$distro" ] || continue ;; Darwin ) target_version="${target_distro#macos }" [ "$target_version" != "$target_distro" ] || continue host_version="${distro#macos }" [[ "$target_version" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]] || continue [[ "$host_version" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]] || continue IFS=. read -r target_major target_minor target_patch <<< "$target_version" IFS=. read -r host_major host_minor host_patch <<< "$host_version" target_patch="${target_patch:-0}" host_patch="${host_patch:-0}" [ "$host_major" -ge "$target_major" ] || continue if [ "$host_major" -eq "$target_major" ]; then [ "$host_minor" -ge "$target_minor" ] || continue if [ "$host_minor" -eq "$target_minor" ]; then [ "$host_patch" -ge "$target_patch" ] || continue fi fi ;; * ) continue ;; esac printf '%s\t%s\t%s\n' "$source_version" "$name" "$sha256" >> "$matches" done < "$manifest" done selection="$(sort -t. -k1,1nr -k2,2nr -k3,3nr "$matches" | head -n1)" IFS=$'\t' read -r version entry expected_sha <<< "$selection" if [ -z "$entry" ]; then echo "pyenv-binary: no binary available for $requested on $os/$arch" >&2 exit 1 fi definitions="$(pyenv-install --list --bare)" || definitions="" if printf '%s\n' "$definitions" | grep -qxF -- "$entry"; then pyenv-install "$entry:$version" exit fi if ! [[ "$expected_sha" =~ ^[0-9a-fA-F]{64}$ ]]; then echo "pyenv-binary: invalid checksum for $entry" >&2 exit 1 fi expected_sha="$(printf '%s' "$expected_sha" | tr '[:upper:]' '[:lower:]')" curl -fsSL -o "$tmpdir/$entry" "$base_url/$entry" if command -v sha256sum >/dev/null 2>&1; then actual_sha="$(sha256sum "$tmpdir/$entry")"; actual_sha="${actual_sha%% *}" elif command -v shasum >/dev/null 2>&1; then actual_sha="$(shasum -a 256 "$tmpdir/$entry")"; actual_sha="${actual_sha%% *}" elif command -v openssl >/dev/null 2>&1; then actual_sha="$(openssl dgst -sha256 "$tmpdir/$entry")"; actual_sha="${actual_sha##* }" else echo "pyenv-binary: need sha256sum, shasum or openssl to verify $entry" >&2 exit 1 fi if [ "$actual_sha" != "$expected_sha" ]; then echo "pyenv-binary: checksum mismatch for $entry" >&2 exit 1 fi pyenv-install "$tmpdir/$entry:$version"