2022-02-06 18:19:28 +01:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
|
|
# Get the curent directory, the script name
|
|
|
|
# and the script name with "py" substituted for the extension.
|
|
|
|
args=( "$@" )
|
2024-03-23 18:34:48 +01:00
|
|
|
dir="$(cd -- "$(dirname "$0")" >/dev/null 2>&1; pwd -P)"
|
2022-02-06 18:19:28 +01:00
|
|
|
script="${0##*/}"
|
|
|
|
target="${script%.*}.py"
|
|
|
|
|
|
|
|
# use_py3:
|
|
|
|
# TRUE = Use if found, use py2 otherwise
|
|
|
|
# FALSE = Use py2
|
|
|
|
# FORCE = Use py3
|
|
|
|
use_py3="TRUE"
|
|
|
|
|
2024-02-07 21:51:04 +01:00
|
|
|
# We'll parse if the first argument passed is
|
|
|
|
# --install-python and if so, we'll just install
|
|
|
|
just_installing="FALSE"
|
|
|
|
|
2022-02-06 18:19:28 +01:00
|
|
|
tempdir=""
|
|
|
|
|
2022-05-10 23:34:02 +02:00
|
|
|
compare_to_version () {
|
|
|
|
# Compares our OS version to the passed OS version, and
|
|
|
|
# return a 1 if we match the passed compare type, or a 0 if we don't.
|
|
|
|
# $1 = 0 (equal), 1 (greater), 2 (less), 3 (gequal), 4 (lequal)
|
|
|
|
# $2 = OS version to compare ours to
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$1" ] || [ -z "$2" ]; then
|
2022-05-10 23:34:02 +02:00
|
|
|
# Missing info - bail.
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
local current_os= comp=
|
|
|
|
current_os="$(sw_vers -productVersion)"
|
2022-05-30 22:13:59 +02:00
|
|
|
comp="$(vercomp "$current_os" "$2")"
|
2022-05-10 23:34:02 +02:00
|
|
|
# Check gequal and lequal first
|
|
|
|
if [[ "$1" == "3" && ("$comp" == "1" || "$comp" == "0") ]] || [[ "$1" == "4" && ("$comp" == "2" || "$comp" == "0") ]] || [[ "$comp" == "$1" ]]; then
|
|
|
|
# Matched
|
2022-05-30 22:13:59 +02:00
|
|
|
echo "1"
|
2022-05-10 23:34:02 +02:00
|
|
|
else
|
|
|
|
# No match
|
2022-05-30 22:13:59 +02:00
|
|
|
echo "0"
|
2022-05-10 23:34:02 +02:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2022-02-06 18:19:28 +01:00
|
|
|
set_use_py3_if () {
|
|
|
|
# Auto sets the "use_py3" variable based on
|
|
|
|
# conditions passed
|
|
|
|
# $1 = 0 (equal), 1 (greater), 2 (less), 3 (gequal), 4 (lequal)
|
|
|
|
# $2 = OS version to compare
|
|
|
|
# $3 = TRUE/FALSE/FORCE in case of match
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Missing vars - bail with no changes.
|
|
|
|
return
|
|
|
|
fi
|
2022-05-10 23:36:44 +02:00
|
|
|
if [ "$(compare_to_version "$1" "$2")" == "1" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
use_py3="$3"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
get_remote_py_version () {
|
|
|
|
local pyurl= py_html= py_vers= py_num="3"
|
|
|
|
pyurl="https://www.python.org/downloads/macos/"
|
2024-03-12 00:14:19 +01:00
|
|
|
py_html="$(curl -L $pyurl --compressed 2>&1)"
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$use_py3" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
use_py3="TRUE"
|
|
|
|
fi
|
|
|
|
if [ "$use_py3" == "FALSE" ]; then
|
|
|
|
py_num="2"
|
|
|
|
fi
|
|
|
|
py_vers="$(echo "$py_html" | grep -i "Latest Python $py_num Release" | awk '{print $8}' | cut -d'<' -f1)"
|
|
|
|
echo "$py_vers"
|
|
|
|
}
|
|
|
|
|
|
|
|
download_py () {
|
|
|
|
local vers="$1" url=
|
|
|
|
clear
|
|
|
|
echo " ### ###"
|
|
|
|
echo " # Downloading Python #"
|
|
|
|
echo "### ###"
|
|
|
|
echo
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$vers" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
echo "Gathering latest version..."
|
|
|
|
vers="$(get_remote_py_version)"
|
|
|
|
fi
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$vers" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Didn't get it still - bail
|
|
|
|
print_error
|
|
|
|
fi
|
|
|
|
echo "Located Version: $vers"
|
|
|
|
echo
|
|
|
|
echo "Building download url..."
|
2024-03-12 00:14:19 +01:00
|
|
|
url="$(curl -L https://www.python.org/downloads/release/python-${vers//./}/ --compressed 2>&1 | grep -iE "python-$vers-macos.*.pkg\"" | awk -F'"' '{ print $2 }')"
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$url" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Couldn't get the URL - bail
|
|
|
|
print_error
|
|
|
|
fi
|
|
|
|
echo " - $url"
|
|
|
|
echo
|
|
|
|
echo "Downloading..."
|
|
|
|
echo
|
|
|
|
# Create a temp dir and download to it
|
|
|
|
tempdir="$(mktemp -d 2>/dev/null || mktemp -d -t 'tempdir')"
|
|
|
|
curl "$url" -o "$tempdir/python.pkg"
|
|
|
|
if [ "$?" != "0" ]; then
|
|
|
|
echo
|
|
|
|
echo " - Failed to download python installer!"
|
|
|
|
echo
|
|
|
|
exit $?
|
|
|
|
fi
|
|
|
|
echo
|
|
|
|
echo "Running python install package..."
|
2024-02-07 21:51:04 +01:00
|
|
|
echo
|
2022-02-06 18:19:28 +01:00
|
|
|
sudo installer -pkg "$tempdir/python.pkg" -target /
|
|
|
|
if [ "$?" != "0" ]; then
|
|
|
|
echo
|
|
|
|
echo " - Failed to install python!"
|
|
|
|
echo
|
|
|
|
exit $?
|
|
|
|
fi
|
2024-02-07 21:51:04 +01:00
|
|
|
# Now we expand the package and look for a shell update script
|
|
|
|
pkgutil --expand "$tempdir/python.pkg" "$tempdir/python"
|
|
|
|
if [ -e "$tempdir/python/Python_Shell_Profile_Updater.pkg/Scripts/postinstall" ]; then
|
|
|
|
# Run the script
|
|
|
|
echo
|
|
|
|
echo "Updating PATH..."
|
|
|
|
echo
|
|
|
|
"$tempdir/python/Python_Shell_Profile_Updater.pkg/Scripts/postinstall"
|
|
|
|
fi
|
2022-02-06 18:19:28 +01:00
|
|
|
vers_folder="Python $(echo "$vers" | cut -d'.' -f1 -f2)"
|
|
|
|
if [ -f "/Applications/$vers_folder/Install Certificates.command" ]; then
|
|
|
|
# Certs script exists - let's execute that to make sure our certificates are updated
|
2024-02-07 21:51:04 +01:00
|
|
|
echo
|
2022-02-06 18:19:28 +01:00
|
|
|
echo "Updating Certificates..."
|
|
|
|
echo
|
|
|
|
"/Applications/$vers_folder/Install Certificates.command"
|
|
|
|
fi
|
2024-02-07 21:51:04 +01:00
|
|
|
echo
|
2022-02-06 18:19:28 +01:00
|
|
|
echo "Cleaning up..."
|
|
|
|
cleanup
|
|
|
|
echo
|
2024-02-07 21:51:04 +01:00
|
|
|
if [ "$just_installing" == "TRUE" ]; then
|
|
|
|
echo "Done."
|
|
|
|
else
|
|
|
|
# Now we check for py again
|
|
|
|
echo "Rechecking py..."
|
|
|
|
downloaded="TRUE"
|
|
|
|
clear
|
|
|
|
main
|
|
|
|
fi
|
2022-02-06 18:19:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cleanup () {
|
|
|
|
if [ -d "$tempdir" ]; then
|
|
|
|
rm -Rf "$tempdir"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
print_error() {
|
|
|
|
clear
|
|
|
|
cleanup
|
|
|
|
echo " ### ###"
|
|
|
|
echo " # Python Not Found #"
|
|
|
|
echo "### ###"
|
|
|
|
echo
|
|
|
|
echo "Python is not installed or not found in your PATH var."
|
|
|
|
echo
|
2024-02-07 21:51:04 +01:00
|
|
|
if [ "$kernel" == "Darwin" ]; then
|
|
|
|
echo "Please go to https://www.python.org/downloads/macos/ to"
|
|
|
|
echo "download and install the latest version, then try again."
|
|
|
|
else
|
|
|
|
echo "Please install python through your package manager and"
|
|
|
|
echo "try again."
|
|
|
|
fi
|
2022-02-06 18:19:28 +01:00
|
|
|
echo
|
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
|
|
|
print_target_missing() {
|
|
|
|
clear
|
|
|
|
cleanup
|
|
|
|
echo " ### ###"
|
|
|
|
echo " # Target Not Found #"
|
|
|
|
echo "### ###"
|
|
|
|
echo
|
|
|
|
echo "Could not locate $target!"
|
|
|
|
echo
|
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
2022-05-30 22:13:59 +02:00
|
|
|
format_version () {
|
|
|
|
local vers="$1"
|
|
|
|
echo "$(echo "$1" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }')"
|
|
|
|
}
|
|
|
|
|
2022-02-06 18:19:28 +01:00
|
|
|
vercomp () {
|
2022-05-30 22:13:59 +02:00
|
|
|
# Modified from: https://apple.stackexchange.com/a/123408/11374
|
|
|
|
local ver1="$(format_version "$1")" ver2="$(format_version "$2")"
|
|
|
|
if [ $ver1 -gt $ver2 ]; then
|
|
|
|
echo "1"
|
|
|
|
elif [ $ver1 -lt $ver2 ]; then
|
|
|
|
echo "2"
|
|
|
|
else
|
2022-02-06 18:19:28 +01:00
|
|
|
echo "0"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
get_local_python_version() {
|
|
|
|
# $1 = Python bin name (defaults to python3)
|
|
|
|
# Echoes the path to the highest version of the passed python bin if any
|
|
|
|
local py_name="$1" max_version= python= python_version= python_path=
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$py_name" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
py_name="python3"
|
|
|
|
fi
|
|
|
|
py_list="$(which -a "$py_name" 2>/dev/null)"
|
|
|
|
# Walk that newline separated list
|
|
|
|
while read python; do
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$python" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Got a blank line - skip
|
|
|
|
continue
|
|
|
|
fi
|
2022-05-10 23:34:02 +02:00
|
|
|
if [ "$check_py3_stub" == "1" ] && [ "$python" == "/usr/bin/python3" ]; then
|
|
|
|
# See if we have a valid developer path
|
|
|
|
xcode-select -p > /dev/null 2>&1
|
|
|
|
if [ "$?" != "0" ]; then
|
|
|
|
# /usr/bin/python3 path - but no valid developer dir
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
2022-05-30 22:13:59 +02:00
|
|
|
python_version="$(get_python_version $python)"
|
|
|
|
if [ -z "$python_version" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Didn't find a py version - skip
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
# Got the py version - compare to our max
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$max_version" ] || [ "$(vercomp "$python_version" "$max_version")" == "1" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Max not set, or less than the current - update it
|
|
|
|
max_version="$python_version"
|
|
|
|
python_path="$python"
|
|
|
|
fi
|
|
|
|
done <<< "$py_list"
|
|
|
|
echo "$python_path"
|
|
|
|
}
|
|
|
|
|
2022-05-30 22:13:59 +02:00
|
|
|
get_python_version() {
|
|
|
|
local py_path="$1" py_version=
|
|
|
|
# Get the python version by piping stderr into stdout (for py2), then grepping the output for
|
|
|
|
# the word "python", getting the second element, and grepping for an alphanumeric version number
|
|
|
|
py_version="$($py_path -V 2>&1 | grep -i python | cut -d' ' -f2 | grep -E "[A-Za-z\d\.]+")"
|
|
|
|
if [ ! -z "$py_version" ]; then
|
|
|
|
echo "$py_version"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2022-02-06 18:19:28 +01:00
|
|
|
prompt_and_download() {
|
2024-02-07 21:51:04 +01:00
|
|
|
if [ "$downloaded" != "FALSE" ] || [ "$kernel" != "Darwin" ]; then
|
|
|
|
# We already tried to download, or we're not on macOS - just bail
|
2022-02-06 18:19:28 +01:00
|
|
|
print_error
|
|
|
|
fi
|
|
|
|
clear
|
|
|
|
echo " ### ###"
|
|
|
|
echo " # Python Not Found #"
|
|
|
|
echo "### ###"
|
|
|
|
echo
|
|
|
|
target_py="Python 3"
|
|
|
|
printed_py="Python 2 or 3"
|
|
|
|
if [ "$use_py3" == "FORCE" ]; then
|
|
|
|
printed_py="Python 3"
|
|
|
|
elif [ "$use_py3" == "FALSE" ]; then
|
|
|
|
target_py="Python 2"
|
|
|
|
printed_py="Python 2"
|
|
|
|
fi
|
|
|
|
echo "Could not locate $printed_py!"
|
|
|
|
echo
|
|
|
|
echo "This script requires $printed_py to run."
|
|
|
|
echo
|
|
|
|
while true; do
|
|
|
|
read -p "Would you like to install the latest $target_py now? (y/n): " yn
|
|
|
|
case $yn in
|
|
|
|
[Yy]* ) download_py;break;;
|
|
|
|
[Nn]* ) print_error;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
main() {
|
2022-05-30 22:13:59 +02:00
|
|
|
local python= version=
|
2022-02-06 18:19:28 +01:00
|
|
|
# Verify our target exists
|
|
|
|
if [ ! -f "$dir/$target" ]; then
|
|
|
|
# Doesn't exist
|
|
|
|
print_target_missing
|
|
|
|
fi
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$use_py3" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
use_py3="TRUE"
|
|
|
|
fi
|
|
|
|
if [ "$use_py3" != "FALSE" ]; then
|
|
|
|
# Check for py3 first
|
|
|
|
python="$(get_local_python_version python3)"
|
|
|
|
fi
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ "$use_py3" != "FORCE" ] && [ -z "$python" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# We aren't using py3 explicitly, and we don't already have a path
|
|
|
|
python="$(get_local_python_version python2)"
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$python" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Try just looking for "python"
|
|
|
|
python="$(get_local_python_version python)"
|
|
|
|
fi
|
|
|
|
fi
|
2022-05-30 22:13:59 +02:00
|
|
|
if [ -z "$python" ]; then
|
2022-02-06 18:19:28 +01:00
|
|
|
# Didn't ever find it - prompt
|
|
|
|
prompt_and_download
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
# Found it - start our script and pass all args
|
|
|
|
"$python" "$dir/$target" "${args[@]}"
|
|
|
|
}
|
|
|
|
|
2024-02-07 21:51:04 +01:00
|
|
|
# Keep track of whether or not we're on macOS to determine if
|
|
|
|
# we can download and install python for the user as needed.
|
|
|
|
kernel="$(uname -s)"
|
2022-02-06 18:19:28 +01:00
|
|
|
# Check to see if we need to force based on
|
|
|
|
# macOS version. 10.15 has a dummy python3 version
|
|
|
|
# that can trip up some py3 detection in other scripts.
|
|
|
|
# set_use_py3_if "3" "10.15" "FORCE"
|
|
|
|
downloaded="FALSE"
|
2022-05-10 23:34:02 +02:00
|
|
|
# Check for the aforementioned /usr/bin/python3 stub if
|
|
|
|
# our OS version is 10.15 or greater.
|
|
|
|
check_py3_stub="$(compare_to_version "3" "10.15")"
|
2022-02-06 18:19:28 +01:00
|
|
|
trap cleanup EXIT
|
2024-02-07 21:51:04 +01:00
|
|
|
if [ "$1" == "--install-python" ] && [ "$kernel" == "Darwin" ]; then
|
|
|
|
just_installing="TRUE"
|
|
|
|
download_py
|
|
|
|
else
|
|
|
|
main
|
|
|
|
fi
|