Files
ray-project--ray/ci/env/install-dependencies.sh
2026-07-13 13:17:40 +08:00

461 lines
17 KiB
Bash
Executable File

#!/usr/bin/env bash
# Push caller's shell options (quietly)
{ SHELLOPTS_STACK="${SHELLOPTS_STACK-}|$(set +o); set -$-"; } 2> /dev/null
set -euxo pipefail
SCRIPT_DIR=$(builtin cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd)
WORKSPACE_DIR="${SCRIPT_DIR}/../.."
# importing install_miniforge function
source "${SCRIPT_DIR}/install-miniforge.sh"
pkg_install_helper() {
case "${OSTYPE}" in
darwin*)
brew install "$@"
;;
linux*)
sudo apt-get install -qq -o=Dpkg::Use-Pty=0 "$@" | {
grep --line-buffered -v "^\(Preparing to unpack\|Unpacking\|Processing triggers for\) "
}
;;
*) false;;
esac
}
install_bazel() {
if command -v bazel; then
if [[ -n "${BUILDKITE-}" && "${OSTYPE}" != "msys" ]]; then
# Only reinstall Bazel if we need to upgrade to a different version.
python="$(command -v python3 || command -v python || echo python)"
current_version="$(bazel --version | grep -o "[0-9]\+.[0-9]\+.[0-9]\+")"
new_version="$(cat "${WORKSPACE_DIR}/.bazelversion")"
if [[ "$current_version" == "$new_version" ]]; then
echo "Bazel of the same version already exists, skipping the install"
export BAZEL_CONFIG_ONLY=1
fi
fi
fi
"${SCRIPT_DIR}"/install-bazel.sh
}
install_base() {
if [[ -n "${BUILDKITE-}" ]]; then
echo "Skipping install_base in Buildkite"
return
fi
case "${OSTYPE}" in
linux*)
# Expired apt key error: https://github.com/bazelbuild/bazel/issues/11470#issuecomment-633205152
curl -f -s -L -R https://bazel.build/bazel-release.pub.gpg | sudo apt-key add - || true
sudo apt-get update -qq
pkg_install_helper build-essential curl unzip libunwind-dev python3-pip python3-setuptools \
tmux gdb
if [[ "${LINUX_WHEELS-}" == 1 ]]; then
pkg_install_helper docker
if [[ -n "${TRAVIS-}" ]]; then
sudo usermod -a -G docker travis
fi
fi
if [[ -n "${PYTHON-}" ]]; then
"${SCRIPT_DIR}/install-strace.sh" || true
fi
;;
esac
}
install_shellcheck() {
local shellcheck_version="0.7.1"
if [[ "${shellcheck_version}" != "$(command -v shellcheck > /dev/null && shellcheck --version | sed -n "s/version: //p")" ]]; then
local osname=""
case "${OSTYPE}" in
linux*) osname="linux";;
darwin*) osname="darwin";;
esac
local name="shellcheck-v${shellcheck_version}"
if [[ "${osname}" == "linux" || "${osname}" == "darwin" ]]; then
sudo mkdir -p /usr/local/bin || true
curl -sSfL "https://github.com/koalaman/shellcheck/releases/download/v${shellcheck_version}/${name}.${osname}.x86_64.tar.xz" | {
sudo tar -C /usr/local/bin -x -v -J --strip-components=1 "${name}/shellcheck"
}
else
mkdir -p /usr/local/bin
curl -sSfL -o "${name}.zip" "https://github.com/koalaman/shellcheck/releases/download/v${shellcheck_version}/${name}.zip"
unzip "${name}.zip" "${name}.exe"
mv -f "${name}.exe" "/usr/local/bin/shellcheck.exe"
fi
test "${shellcheck_version}" = "$(shellcheck --version | sed -n "s/version: //p")"
fi
}
install_linters() {
pip install -r "${WORKSPACE_DIR}"/python/requirements/lint-requirements.txt
install_shellcheck
}
install_nvm() {
local NVM_HOME="${HOME}/.nvm"
if [[ "${OSTYPE}" == msys ]]; then
local ver="1.1.7"
if [[ ! -f "${NVM_HOME}/nvm.sh" ]]; then
mkdir -p -- "${NVM_HOME}"
export NVM_SYMLINK="${PROGRAMFILES}\nodejs"
(
cd "${NVM_HOME}"
local target="./nvm-${ver}.zip"
curl -sSfL -o "${target}" \
"https://github.com/coreybutler/nvm-windows/releases/download/${ver}/nvm-noinstall.zip"
unzip -q -- "${target}"
rm -f -- "${target}"
printf "%s\r\n" "root: $(cygpath -w -- "${NVM_HOME}")" "path: ${NVM_SYMLINK}" > settings.txt
)
printf "%s\n" \
"export NVM_HOME=\"$(cygpath -w -- "${NVM_HOME}")\"" \
"nvm() { \"\${NVM_HOME}/nvm.exe\" \"\$@\"; }" \
> "${NVM_HOME}/nvm.sh"
fi
elif [[ -n "${BUILDKITE-}" ]]; then
echo "Skipping nvm on Buildkite because we will use apt-get."
else
test -f "${NVM_HOME}/nvm.sh" # double-check NVM is already available on other platforms
fi
}
install_upgrade_pip() {
local python=python
if command -v python3 > /dev/null; then
python=python3
fi
if "${python}" -m pip --version || "${python}" -m ensurepip; then # Configure pip if present
# 25.3 has breaking change where other Python packages like "click" does not work
# with it anymore. pip-compile will fail to work with the package's setup code.
"${python}" -m pip install pip==25.2
# If we're in a CI environment, do some configuration
if [[ "${CI-}" == "true" ]]; then
"${python}" -W ignore -m pip config -q --user set global.disable-pip-version-check True
"${python}" -W ignore -m pip config -q --user set global.progress_bar off
fi
"${python}" -m ensurepip
fi
}
install_node() {
if [[ "${OSTYPE}" == "msys" ]]; then
{ echo "WARNING: Skipping running Node.js due to incompatibilities with Windows"; } 2> /dev/null
return
fi
if [[ "${BUILDKITE-}" == "" ]] ; then
echo "Skipping install_node in non-Buildkite environment is not supported" > /dev/stderr
return
fi
if [[ "${OSTYPE}" = darwin* ]]; then
curl -sSfL -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
(
set +x # suppress set -x since it'll get very noisy here.
# nvm's error-recovery path in nvm.sh references $TMPDIR. The caller
# runs with `set -u`, so an unset TMPDIR (launchd-spawned shells don't
# inherit one) crashes nvm with "TMPDIR: unbound variable" whenever a
# binary download fails and nvm falls back to source.
export TMPDIR="${TMPDIR:-/tmp}"
. "${HOME}/.nvm/nvm.sh"
# Node 14 EOL'd April 2023; nodejs.org removed the darwin-arm64
# prebuilt and the URL now 404s. 20 is the current LTS line.
NODE_VERSION="20"
nvm install $NODE_VERSION
nvm use --silent $NODE_VERSION
npm config set loglevel warn # make NPM quieter
)
else
# Linux
NODE_VERSION_FULL="14.21.3"
if [[ "$(uname -m)" == "x86_64" ]]; then
NODE_URL="https://nodejs.org/dist/v${NODE_VERSION_FULL}/node-v${NODE_VERSION_FULL}-linux-x64.tar.xz"
NODE_SHA256="05c08a107c50572ab39ce9e8663a2a2d696b5d262d5bd6f98d84b997ce932d9a"
else # aarch64
NODE_URL="https://nodejs.org/dist/v${NODE_VERSION_FULL}/node-v${NODE_VERSION_FULL}-linux-arm64.tar.xz"
NODE_SHA256="f06642bfcf0b8cc50231624629bec58b183954641b638e38ed6f94cd39e8a6ef"
fi
NODE_DIR="/usr/local/node"
curl -fsSL "${NODE_URL}" -o /tmp/node.tar.xz
echo "$NODE_SHA256 /tmp/node.tar.xz" | sha256sum -c -
sudo mkdir -p "$NODE_DIR"
sudo tar -xf /tmp/node.tar.xz -C "$NODE_DIR" --strip-components=1
rm /tmp/node.tar.xz
sudo ln -sf /usr/local/node/bin/node /usr/local/bin/node
sudo ln -sf /usr/local/node/bin/npm /usr/local/bin/npm
sudo ln -sf /usr/local/node/bin/npx /usr/local/bin/npx
fi
}
install_toolchains() {
if [[ -z "${BUILDKITE-}" ]]; then
"${SCRIPT_DIR}"/install-toolchains.sh
fi
if [[ "${OSTYPE}" = linux* ]]; then
pushd "${WORKSPACE_DIR}"
"${SCRIPT_DIR}"/install-llvm-binaries.sh
popd
fi
}
download_mnist() {
if [[ -d "${HOME}/data/MNIST" ]]; then
return
fi
mkdir -p "${HOME}/data"
curl -o "${HOME}/data/mnist.zip" https://ray-ci-mnist.s3-us-west-2.amazonaws.com/mnist.zip
unzip "${HOME}/data/mnist.zip" -d "${HOME}/data"
}
retry_pip_install() {
local status="0"
local errmsg=""
# Try n times; we often encounter OpenSSL.SSL.WantReadError (or others)
# that break the entire CI job: Simply retry installation in this case
# after n seconds.
for _ in {1..3}; do
errmsg="$("$@" 2>&1)" && break
status=$errmsg && echo "'pip install ...' failed, will retry after n seconds!" && sleep 30
done
if [[ "$status" != "0" ]]; then
echo "${status}" && return 1
fi
}
install_pip_packages() {
# Install modules needed in all jobs.
# shellcheck disable=SC2262
alias pip="python -m pip"
# Array to hold all requirements files to install later
requirements_files=()
# Single packages to install in sync with files
requirements_packages=()
# Packages to install _after_ previous files have been installed
# (e.g. to install a custom pyarrow or torch version). This
# would otherwise conflict with pinned dependencies in our requirements
# files.
delayed_packages=()
requirements_files+=("${WORKSPACE_DIR}/python/requirements/test-requirements.txt")
if [[ "${LINT-}" == 1 ]]; then
install_linters
requirements_files+=("${WORKSPACE_DIR}/doc/requirements-doc.txt")
fi
# Additional default doc testing dependencies.
if [[ "${DOC_TESTING-}" == 1 ]]; then
# For Ray Core and Ray Serve DAG visualization docs test + dataset examples
sudo apt-get install -y graphviz tesseract-ocr
# For DAG visualization
requirements_packages+=("pydot")
requirements_packages+=("pytesseract==0.3.13")
fi
# Additional RLlib test dependencies.
if [[ "${RLLIB_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/rllib-requirements.txt")
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/rllib-test-requirements.txt")
# Install MuJoCo.
sudo apt-get update
sudo apt-get install -y libosmesa6-dev libgl1 libglfw3 patchelf
wget https://github.com/google-deepmind/mujoco/releases/download/2.1.1/mujoco-2.1.1-linux-x86_64.tar.gz
mkdir -p /root/.mujoco
mv mujoco-2.1.1-linux-x86_64.tar.gz /root/.mujoco/.
(cd /root/.mujoco && tar -xf /root/.mujoco/mujoco-2.1.1-linux-x86_64.tar.gz)
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:/root/.mujoco/mujoco-2.1.1/bin
fi
# Additional Train test dependencies.
if [[ "${TRAIN_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/train-requirements.txt")
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/train-test-requirements.txt")
fi
# Additional Tune/Doc test dependencies.
if [[ "${TUNE_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/tune-requirements.txt")
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/tune-test-requirements.txt")
fi
# Data processing test dependencies.
if [[ "${DATA_PROCESSING_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/data-requirements.txt")
fi
if [[ "${DATA_PROCESSING_TESTING-}" == 1 ]]; then
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/data-test-requirements.txt")
if [[ -n "${ARROW_VERSION-}" ]]; then
if [[ "${ARROW_VERSION-}" == "nightly" ]]; then
delayed_packages+=("--extra-index-url")
delayed_packages+=("https://pypi.fury.io/arrow-nightlies/")
delayed_packages+=("--prefer-binary")
delayed_packages+=("--pre")
delayed_packages+=("pyarrow")
else
delayed_packages+=("pyarrow==${ARROW_VERSION}")
fi
fi
if [[ -n "${ARROW_MONGO_VERSION-}" ]]; then
delayed_packages+=("pymongoarrow==${ARROW_MONGO_VERSION}")
fi
fi
# TODO(ray-ci): pin the dependencies.
CC=gcc retry_pip_install pip install -Ur "${WORKSPACE_DIR}/python/requirements.txt" -c "${WORKSPACE_DIR}/python/requirements_compiled.txt"
# Install deeplearning libraries (Torch + TensorFlow)
if [[ -n "${TORCH_VERSION-}" || "${DL-}" == "1" || "${RLLIB_TESTING-}" == 1 || "${TRAIN_TESTING-}" == 1 || "${TUNE_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
# If we require a custom torch version, use that
if [[ -n "${TORCH_VERSION-}" ]]; then
# Install right away, as some dependencies (e.g. torch-spline-conv) need
# torch to be installed for their own install.
pip install -U "torch==${TORCH_VERSION-1.9.0}" "torchvision==${TORCHVISION_VERSION-0.10.0}"
# We won't add dl-cpu-requirements.txt as it would otherwise overwrite our custom
# torch. Thus we have also have to install tensorflow manually.
TF_PACKAGE=$(grep -ohE "tensorflow==[^ ;]+" "${WORKSPACE_DIR}/python/requirements/ml/dl-cpu-requirements.txt" | head -n 1)
TFPROB_PACKAGE=$(grep -ohE "tensorflow-probability==[^ ;]+" "${WORKSPACE_DIR}/python/requirements/ml/dl-cpu-requirements.txt" | head -n 1)
# %%;* deletes everything after ; to get rid of e.g. python version specifiers
pip install -U "${TF_PACKAGE%%;*}" "${TFPROB_PACKAGE%%;*}"
else
# Otherwise, use pinned default torch version.
# Again, install right away, as some dependencies (e.g. torch-spline-conv) need
# torch to be installed for their own install.
TORCH_PACKAGE=$(grep -ohE "torch==[^ ;]+" "${WORKSPACE_DIR}/python/requirements/ml/dl-cpu-requirements.txt" | head -n 1)
TORCHVISION_PACKAGE=$(grep -ohE "torchvision==[^ ;]+" "${WORKSPACE_DIR}/python/requirements/ml/dl-cpu-requirements.txt" | head -n 1)
# %%;* deletes everything after ; to get rid of e.g. python version specifiers
pip install "${TORCH_PACKAGE%%;*}" "${TORCHVISION_PACKAGE%%;*}"
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/dl-cpu-requirements.txt")
fi
fi
# AIR core dependencies
if [[ "${RLLIB_TESTING-}" = 1 || "${TRAIN_TESTING-}" == 1 || "${TUNE_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
requirements_files+=("${WORKSPACE_DIR}/python/requirements/ml/core-requirements.txt")
fi
# Inject our own mirror for the CIFAR10 dataset
if [[ "${TRAIN_TESTING-}" = 1 || "${TUNE_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
SITE_PACKAGES=$(python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')
TF_CIFAR="${SITE_PACKAGES}/tensorflow/python/keras/datasets/cifar10.py"
TORCH_CIFAR="${SITE_PACKAGES}/torchvision/datasets/cifar.py"
[ -f "$TF_CIFAR" ] && sed -i 's https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz https://air-example-data.s3.us-west-2.amazonaws.com/cifar-10-python.tar.gz g' \
"$TF_CIFAR"
[ -f "$TORCH_CIFAR" ] &&sed -i 's https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz https://air-example-data.s3.us-west-2.amazonaws.com/cifar-10-python.tar.gz g' \
"$TORCH_CIFAR"
fi
# Generate the pip command with collected requirements files
pip_cmd="pip install -U -c ${WORKSPACE_DIR}/python/requirements.txt"
if [[ -f "${WORKSPACE_DIR}/python/requirements_compiled.txt" && "${OSTYPE}" != "msys" ]]; then
# On Windows, some pinned dependencies are not built for win, so we
# skip this until we have a good wy to resolve cross-platform dependencies.
pip_cmd+=" -c ${WORKSPACE_DIR}/python/requirements_compiled.txt"
fi
for file in "${requirements_files[@]}"; do
pip_cmd+=" -r ${file}"
done
# Expand single requirements
if [[ "${#requirements_packages[@]}" -gt 0 ]]; then
pip_cmd+=" ${requirements_packages[*]}"
fi
# Install
eval "${pip_cmd}"
# Install delayed packages
if [[ "${#delayed_packages[@]}" -gt 0 ]]; then
pip install -U -c "${WORKSPACE_DIR}/python/requirements.txt" "${delayed_packages[@]}"
fi
# Additional Tune dependency for Horovod.
# This must be run last (i.e., torch cannot be re-installed after this)
if [[ "${INSTALL_HOROVOD-}" == 1 ]]; then
"${SCRIPT_DIR}"/install-horovod.sh
fi
if [[ "${TUNE_TESTING-}" == 1 || "${DOC_TESTING-}" == 1 ]]; then
download_mnist
fi
}
install_thirdparty_packages() {
if [[ "${OSTYPE}" = darwin* ]]; then
# Currently do not work on macOS
return
fi
mkdir -p "${WORKSPACE_DIR}/python/ray/thirdparty_files"
RAY_THIRDPARTY_FILES="$(realpath "${WORKSPACE_DIR}/python/ray/thirdparty_files")"
CC=gcc python -m pip install psutil==5.9.6 colorama==0.4.6 --target="${RAY_THIRDPARTY_FILES}"
}
install_dependencies() {
install_bazel
# Only install on buildkite if requested
if [[ -z "${BUILDKITE-}" || "${BUILD-}" == "1" ]]; then
install_base
install_toolchains
fi
if [[ -n "${PYTHON-}" || "${LINT-}" == 1 || "${MINIMAL_INSTALL-}" == "1" ]]; then
install_miniforge
fi
install_upgrade_pip
# Only install on buildkite if requested
if [[ -z "${BUILDKITE-}" || "${BUILD-}" == "1" ]]; then
install_nvm
if [[ -n "${PYTHON-}" || -n "${LINT-}" || "${MAC_WHEELS-}" == 1 ]]; then
install_node
fi
fi
# install hdfs if needed.
if [[ "${INSTALL_HDFS-}" == 1 ]]; then
"${SCRIPT_DIR}"/install-hdfs.sh
fi
if [[ "${MINIMAL_INSTALL:-}" != "1" && "${SKIP_PYTHON_PACKAGES:-}" != "1" && "${SKIP_PIP_INSTALL:-}" != "1" ]]; then
install_pip_packages
fi
install_thirdparty_packages
}
if [[ $# -eq 0 ]]; then
install_dependencies
else
"$@"
fi
# Pop caller's shell options (quietly)
{ set -vx; eval "${SHELLOPTS_STACK##*|}"; SHELLOPTS_STACK="${SHELLOPTS_STACK%|*}"; } 2> /dev/null