#!/usr/bin/env bash # Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). # =============================== NOTE =============================== # This ./pants bootstrap script comes from the pantsbuild/setup # project. It is intended to be checked into your code repository so # that other developers have the same setup. # # Learn more here: https://www.pantsbuild.org/docs/installation # ==================================================================== set -eou pipefail # NOTE: To use an unreleased version of Pants from the pantsbuild/pants main branch, # locate the main branch SHA, set PANTS_SHA= in the environment, and run this script as usual. # # E.g., PANTS_SHA=725fdaf504237190f6787dda3d72c39010a4c574 ./pants --version PYTHON_BIN_NAME="${PYTHON:-unspecified}" # Set this to specify a non-standard location for this script to read the Pants version from. # NB: This will *not* cause Pants itself to use this location as a config file. # You can use PANTS_CONFIG_FILES or --pants-config-files to do so. PANTS_TOML=${PANTS_TOML:-pants.toml} PANTS_BIN_NAME="${PANTS_BIN_NAME:-$0}" PANTS_SETUP_CACHE="${PANTS_SETUP_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/pants/setup}" # If given a relative path, we fix it to be absolute. if [[ "$PANTS_SETUP_CACHE" != /* ]]; then PANTS_SETUP_CACHE="${PWD}/${PANTS_SETUP_CACHE}" fi PANTS_BOOTSTRAP="${PANTS_SETUP_CACHE}/bootstrap-$(uname -s)-$(uname -m)" _PEX_VERSION=2.1.42 _PEX_URL="https://github.com/pantsbuild/pex/releases/download/v${_PEX_VERSION}/pex" _PEX_EXPECTED_SHA256="69d6b1b1009b00dd14a3a9f19b72cff818a713ca44b3186c9b12074b2a31e51f" VIRTUALENV_VERSION=20.4.7 VIRTUALENV_REQUIREMENTS=$( cat << EOF virtualenv==${VIRTUALENV_VERSION} --hash sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76 filelock==3.0.12 --hash sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 six==1.16.0 --hash sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 distlib==0.3.2 --hash sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c appdirs==1.4.4 --hash sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128 importlib-resources==5.1.4; python_version < "3.7" --hash sha256:e962bff7440364183203d179d7ae9ad90cb1f2b74dcb84300e88ecc42dca3351 importlib-metadata==4.5.0; python_version < "3.8" --hash sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00 zipp==3.4.1; python_version < "3.10" --hash sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098 typing-extensions==3.10.0.0; python_version < "3.8" --hash sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 EOF ) COLOR_RED="\x1b[31m" COLOR_GREEN="\x1b[32m" COLOR_YELLOW="\x1b[33m" COLOR_RESET="\x1b[0m" function log() { echo -e "$@" 1>&2 } function die() { (($# > 0)) && log "${COLOR_RED}$*${COLOR_RESET}" exit 1 } function green() { (($# > 0)) && log "${COLOR_GREEN}$*${COLOR_RESET}" } function warn() { (($# > 0)) && log "${COLOR_YELLOW}$*${COLOR_RESET}" } function tempdir { mkdir -p "$1" mktemp -d "$1"/pants.XXXXXX } function get_exe_path_or_die { local exe="$1" if ! command -v "${exe}"; then die "Could not find ${exe}. Please ensure ${exe} is on your PATH." fi } function get_pants_config_value { local config_key="$1" local optional_space="[[:space:]]*" local prefix="^${config_key}${optional_space}=${optional_space}" local raw_value raw_value="$(sed -ne "/${prefix}/ s#${prefix}##p" "${PANTS_TOML}")" echo "${raw_value}" | tr -d \"\' && return 0 return 0 } function get_python_major_minor_version { local python_exe="$1" "$python_exe" <&1 > /dev/null)" == "pyenv: python${version}"* ]]; then continue fi if [[ -n "$(check_python_exe_compatible_version "${interpreter_path}")" ]]; then echo "${interpreter_path}" && return 0 fi done } function determine_python_exe { local pants_version="$1" set_supported_python_versions "${pants_version}" local requirement_str="For \`pants_version = \"${pants_version}\"\`, Pants requires Python ${supported_message} to run." local python_exe if [[ "${PYTHON_BIN_NAME}" != 'unspecified' ]]; then python_exe="$(get_exe_path_or_die "${PYTHON_BIN_NAME}")" || exit 1 if [[ -z "$(check_python_exe_compatible_version "${python_exe}")" ]]; then die "Invalid Python interpreter version for ${python_exe}. ${requirement_str}" fi else python_exe="$(determine_default_python_exe)" if [[ -z "${python_exe}" ]]; then die "No valid Python interpreter found. ${requirement_str} Please check that a valid interpreter is installed and on your \$PATH." fi fi echo "${python_exe}" } function compute_sha256 { local python="$1" local path="$2" "$python" <&2 || exit 1 fi echo "${bootstrapped}" } function scrub_PEX_env_vars { # Ensure the virtualenv PEX runs as shrink-wrapped. # See: https://github.com/pantsbuild/setup/issues/105 if [[ -n "${!PEX_@}" ]]; then warn "Scrubbing ${!PEX_@}" unset "${!PEX_@}" fi } function bootstrap_virtualenv { local python="$1" local bootstrapped="${PANTS_BOOTSTRAP}/virtualenv-${VIRTUALENV_VERSION}/virtualenv.pex" if [[ ! -f "${bootstrapped}" ]]; then ( green "Creating the virtualenv PEX." pex_path="$(bootstrap_pex "${python}")" || exit 1 mkdir -p "${PANTS_BOOTSTRAP}" local staging_dir staging_dir=$(tempdir "${PANTS_BOOTSTRAP}") cd "${staging_dir}" echo "${VIRTUALENV_REQUIREMENTS}" > requirements.txt ( scrub_PEX_env_vars "${python}" "${pex_path}" -r requirements.txt -c virtualenv -o virtualenv.pex ) mkdir -p "$(dirname "${bootstrapped}")" mv -f "${staging_dir}/virtualenv.pex" "${bootstrapped}" rm -rf "${staging_dir}" ) 1>&2 || exit 1 fi echo "${bootstrapped}" } function find_links_url { local pants_version="$1" local pants_sha="$2" echo -n "https://binaries.pantsbuild.org/wheels/pantsbuild.pants/${pants_sha}/${pants_version/+/%2B}/index.html" } function get_version_for_sha { local sha="$1" # Retrieve the Pants version associated with this commit. local pants_version pants_version="$(curl --fail -sL "https://raw.githubusercontent.com/pantsbuild/pants/${sha}/src/python/pants/VERSION")" # Construct the version as the release version from src/python/pants/VERSION, plus the string `+gitXXXXXXXX`, # where the XXXXXXXX is the first 8 characters of the SHA. echo "${pants_version}+git${sha:0:8}" } function bootstrap_pants { local pants_version="$1" local python="$2" local pants_sha="${3:-}" local pants_requirement="pantsbuild.pants==${pants_version}" local maybe_find_links if [[ -z "${pants_sha}" ]]; then maybe_find_links="" else maybe_find_links="--find-links=$(find_links_url "${pants_version}" "${pants_sha}")" fi local python_major_minor_version python_major_minor_version="$(get_python_major_minor_version "${python}")" local target_folder_name="${pants_version}_py${python_major_minor_version}" local bootstrapped="${PANTS_BOOTSTRAP}/${target_folder_name}" if [[ ! -d "${bootstrapped}" ]]; then ( green "Bootstrapping Pants using ${python}" local staging_dir staging_dir=$(tempdir "${PANTS_BOOTSTRAP}") local virtualenv_path virtualenv_path="$(bootstrap_virtualenv "${python}")" || exit 1 green "Installing ${pants_requirement} into a virtual environment at ${bootstrapped}" ( scrub_PEX_env_vars # shellcheck disable=SC2086 "${python}" "${virtualenv_path}" --no-download "${staging_dir}/install" && \ "${staging_dir}/install/bin/pip" install -U pip && \ "${staging_dir}/install/bin/pip" install ${maybe_find_links} --progress-bar off "${pants_requirement}" ) && \ ln -s "${staging_dir}/install" "${staging_dir}/${target_folder_name}" && \ mv "${staging_dir}/${target_folder_name}" "${bootstrapped}" && \ green "New virtual environment successfully created at ${bootstrapped}." ) 1>&2 || exit 1 fi echo "${bootstrapped}" } # Ensure we operate from the context of the ./pants buildroot. cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" pants_version="$(determine_pants_version)" python="$(determine_python_exe "${pants_version}")" pants_dir="$(bootstrap_pants "${pants_version}" "${python}" "${PANTS_SHA:-}")" || exit 1 pants_python="${pants_dir}/bin/python" pants_binary="${pants_dir}/bin/pants" pants_extra_args="" if [[ -n "${PANTS_SHA:-}" ]]; then pants_extra_args="${pants_extra_args} --python-repos-repos=$(find_links_url "$pants_version" "$PANTS_SHA")" fi # shellcheck disable=SC2086 exec "${pants_python}" "${pants_binary}" ${pants_extra_args} \ --pants-bin-name="${PANTS_BIN_NAME}" --pants-version=${pants_version} "$@"