1212
1313set -eou pipefail
1414
15+ # an arbitrary number: bump when there's a change that someone might want to query for
16+ # (e.g. checking $(PANTS_BOOTSTRAP_TOOLS=1 ./pants version) >= ...)
17+ SCRIPT_VERSION=1
18+
19+ # Source any custom bootstrap settings for Pants from PANTS_BOOTSTRAP if it exists.
20+ : ${PANTS_BOOTSTRAP:= " .pants.bootstrap" }
21+ if [[ -f " ${PANTS_BOOTSTRAP} " ]]; then
22+ source " ${PANTS_BOOTSTRAP} "
23+ fi
24+
1525# NOTE: To use an unreleased version of Pants from the pantsbuild/pants main branch,
1626# locate the main branch SHA, set PANTS_SHA=<SHA> in the environment, and run this script as usual.
1727#
1828# E.g., PANTS_SHA=725fdaf504237190f6787dda3d72c39010a4c574 ./pants --version
29+ #
30+ # You can also use PANTS_VERSION=<VERSION> to override the config version that is in the pants.toml file.
31+ #
32+ # E.g., PANTS_VERSION=2.13.0 ./pants --version
1933
2034PYTHON_BIN_NAME=" ${PYTHON:- unspecified} "
2135
3448
3549PANTS_BOOTSTRAP=" ${PANTS_SETUP_CACHE} /bootstrap-$( uname -s) -$( uname -m) "
3650
37- _PEX_VERSION=2.1.62
51+ _PEX_VERSION=2.1.103
3852_PEX_URL=" https://github.com/pantsbuild/pex/releases/download/v${_PEX_VERSION} /pex"
39- _PEX_EXPECTED_SHA256=" 56668b1ca147bd63141e586ffee97c7cc51ce8e6eac6c9b7a4bf1215b94396e5 "
53+ _PEX_EXPECTED_SHA256=" 4d45336511484100ae4e2bab24542a8b86b12c8cb89230463593c60d08c4b8d3 "
4054
4155VIRTUALENV_VERSION=20.4.7
4256VIRTUALENV_REQUIREMENTS=$(
@@ -58,6 +72,8 @@ COLOR_GREEN="\x1b[32m"
5872COLOR_YELLOW=" \x1b[33m"
5973COLOR_RESET=" \x1b[0m"
6074
75+ INSTALL_URL=" https://www.pantsbuild.org/docs/installation"
76+
6177function log() {
6278 echo -e " $@ " 1>&2
6379}
@@ -130,11 +146,16 @@ function determine_pants_version {
130146 return
131147 fi
132148
149+ if [ -n " ${PANTS_VERSION:- } " ]; then
150+ echo " ${PANTS_VERSION} "
151+ return
152+ fi
153+
133154 pants_version=" $( get_pants_config_string_value ' pants_version' ) "
134155 if [[ -z " ${pants_version} " ]]; then
135156 die " Please explicitly specify the \` pants_version\` in your \` pants.toml\` under the \` [GLOBAL]\` scope.
136157See https://pypi.org/project/pantsbuild.pants/#history for all released versions
137- and https://www.pantsbuild.org/docs/installation for more instructions."
158+ and ${INSTALL_URL} for more instructions."
138159 fi
139160 pants_major_version=" $( echo " ${pants_version} " | cut -d ' .' -f1) "
140161 pants_minor_version=" $( echo " ${pants_version} " | cut -d ' .' -f2) "
@@ -253,14 +274,13 @@ function bootstrap_pex {
253274 mkdir -p " ${PANTS_BOOTSTRAP} "
254275 local staging_dir
255276 staging_dir=$( tempdir " ${PANTS_BOOTSTRAP} " )
256- cd " ${staging_dir} "
257277 curl --proto " =https" \
258278 --tlsv1.2 \
259279 --silent \
260280 --location \
261- --remote-name \
281+ -o " ${staging_dir} /pex " \
262282 " ${_PEX_URL} "
263- fingerprint=" $( compute_sha256 " ${python} " " pex" ) "
283+ fingerprint=" $( compute_sha256 " ${python} " " ${staging_dir} / pex" ) "
264284 if [[ " ${_PEX_EXPECTED_SHA256} " != " ${fingerprint} " ]]; then
265285 die " SHA256 of ${_PEX_URL} is not as expected. Aborting."
266286 fi
@@ -273,12 +293,22 @@ function bootstrap_pex {
273293 echo " ${bootstrapped} "
274294}
275295
276- function scrub_PEX_env_vars {
296+ function scrub_env_vars {
277297 # Ensure the virtualenv PEX runs as shrink-wrapped.
278298 # See: https://github.com/pantsbuild/setup/issues/105
279- if [[ -n " ${! PEX_@ } " ]]; then
280- warn " Scrubbing ${! PEX_@ } "
281- unset " ${! PEX_@ } "
299+ local -r pex_env_vars=(${! PEX_@ } )
300+ if [[ ! ${# pex_env_vars[@]} -eq 0 ]]; then
301+ local -r pex_env_vars_to_scrub=" ${pex_env_vars[@]/ PEX_ROOT} "
302+ if [[ -n " ${pex_env_vars_to_scrub[@]} " ]]; then
303+ warn " Scrubbing ${pex_env_vars_to_scrub[@]} "
304+ unset ${pex_env_vars_to_scrub[@]}
305+ fi
306+ fi
307+ # Also ensure pip doesn't think packages on PYTHONPATH
308+ # are already installed.
309+ if [ -n " ${PYTHONPATH:- } " ]; then
310+ warn " Scrubbing PYTHONPATH"
311+ unset PYTHONPATH
282312 fi
283313}
284314
@@ -292,11 +322,10 @@ function bootstrap_virtualenv {
292322 mkdir -p " ${PANTS_BOOTSTRAP} "
293323 local staging_dir
294324 staging_dir=$( tempdir " ${PANTS_BOOTSTRAP} " )
295- cd " ${staging_dir} "
296- echo " ${VIRTUALENV_REQUIREMENTS} " > requirements.txt
325+ echo " ${VIRTUALENV_REQUIREMENTS} " > " ${staging_dir} /requirements.txt"
297326 (
298- scrub_PEX_env_vars
299- " ${python} " " ${pex_path} " -r requirements.txt -c virtualenv -o virtualenv.pex
327+ scrub_env_vars
328+ " ${python} " " ${pex_path} " -r " ${staging_dir} / requirements.txt" -c virtualenv -o " ${staging_dir} / virtualenv.pex"
300329 )
301330 mkdir -p " $( dirname " ${bootstrapped} " ) "
302331 mv -f " ${staging_dir} /virtualenv.pex" " ${bootstrapped} "
@@ -333,17 +362,27 @@ function bootstrap_pants {
333362 local pants_version=" $1 "
334363 local python=" $2 "
335364 local pants_sha=" ${3:- } "
365+ local pants_debug=" ${4:- } "
336366
337- local pants_requirement= " pantsbuild.pants==${pants_version} "
367+ local pants_requirements=( pantsbuild.pants==${pants_version} )
338368 local maybe_find_links
339369 if [[ -z " ${pants_sha} " ]]; then
340370 maybe_find_links=" "
341371 else
342372 maybe_find_links=" --find-links=$( find_links_url " ${pants_version} " " ${pants_sha} " ) "
343- fi
373+ fi
374+
375+ local debug_suffix
376+ if [[ -z " ${pants_debug} " ]]; then
377+ debug_suffix=" "
378+ else
379+ debug_suffix=" -debug"
380+ pants_requirements+=(debugpy==1.6.0)
381+ fi
382+
344383 local python_major_minor_version
345384 python_major_minor_version=" $( get_python_major_minor_version " ${python} " ) "
346- local target_folder_name=" ${pants_version} _py${python_major_minor_version} "
385+ local target_folder_name=" ${pants_version} _py${python_major_minor_version}${debug_suffix} "
347386 local bootstrapped=" ${PANTS_BOOTSTRAP} /${target_folder_name} "
348387
349388 if [[ ! -d " ${bootstrapped} " ]]; then
@@ -353,15 +392,15 @@ function bootstrap_pants {
353392 staging_dir=$( tempdir " ${PANTS_BOOTSTRAP} " )
354393 local virtualenv_path
355394 virtualenv_path=" $( bootstrap_virtualenv " ${python} " ) " || exit 1
356- green " Installing ${pants_requirement } into a virtual environment at ${bootstrapped} "
395+ green " Installing ${pants_requirements[@] } into a virtual environment at ${bootstrapped} "
357396 (
358- scrub_PEX_env_vars
397+ scrub_env_vars
359398 # shellcheck disable=SC2086
360399 " ${python} " " ${virtualenv_path} " --quiet --no-download " ${staging_dir} /install" && \
361400 # Grab the latest pip, but don't advance setuptools past 58 which drops support for the
362401 # `setup` kwarg `use_2to3` which Pants 1.x sdist dependencies (pystache) use.
363402 " ${staging_dir} /install/bin/pip" install --quiet -U pip " setuptools<58" && \
364- " ${staging_dir} /install/bin/pip" install ${maybe_find_links} --quiet --progress-bar off " ${pants_requirement } "
403+ " ${staging_dir} /install/bin/pip" install ${maybe_find_links} --quiet --progress-bar off " ${pants_requirements[@] } "
365404 ) && \
366405 ln -s " ${staging_dir} /install" " ${staging_dir} /${target_folder_name} " && \
367406 mv " ${staging_dir} /${target_folder_name} " " ${bootstrapped} " && \
@@ -371,19 +410,101 @@ function bootstrap_pants {
371410 echo " ${bootstrapped} "
372411}
373412
413+ function run_bootstrap_tools {
414+ # functionality for introspecting the bootstrapping process, without actually doing it
415+ if [[ " ${PANTS_BOOTSTRAP_TOOLS} " -gt " ${SCRIPT_VERSION} " ]]; then
416+ die " $0 script (bootstrap version ${SCRIPT_VERSION} ) is too old for this invocation (with PANTS_BOOTSTRAP_TOOLS=${PANTS_BOOTSTRAP_TOOLS} ).
417+ Please update it by following ${INSTALL_URL} "
418+ fi
419+
420+ case " ${1:- } " in
421+ bootstrap-cache-key)
422+ local pants_version=$( determine_pants_version)
423+ local python=" $( determine_python_exe " ${pants_version} " ) "
424+ # the python above may be a shim (e.g. pyenv or homebrew), so let's get an estimate of the
425+ # actual path, as will be symlinked in the virtualenv. (NB. virtualenv does more complicated
426+ # things, but we at least emulate the symlink-resolution that it does.)
427+ local python_executable_path=" $( " ${python} " -c ' import os, sys; print(os.path.realpath(sys.executable))' ) "
428+
429+ local requirements_file=" $( mktemp) "
430+ echo " ${VIRTUALENV_REQUIREMENTS} " > " ${requirements_file} "
431+ local virtualenv_requirements_sha256=" $( compute_sha256 " ${python} " " ${requirements_file} " ) "
432+ rm " ${requirements_file} "
433+
434+ local parts=(
435+ " os_name=$( uname -s) "
436+ " arch=$( uname -m) "
437+ " python_path=${python} "
438+ " python_executable_path=${python_executable_path} "
439+ # the full interpreter information, for maximum compatibility
440+ " python_version=$( " $python " --version) "
441+ " pex_version=${_PEX_VERSION} "
442+ " virtualenv_requirements_sha256=${virtualenv_requirements_sha256} "
443+ " pants_version=${pants_version} "
444+ )
445+ echo " ${parts[*]} "
446+ ;;
447+ bootstrap-version)
448+ echo " ${SCRIPT_VERSION} "
449+ ;;
450+ help|" " )
451+ cat << EOF
452+ Usage: PANTS_BOOTSTRAP_TOOLS=1 $0 ...
453+
454+ Subcommands:
455+ bootstrap-cache-key
456+ Print an opaque that can be used as a key for accurate and safe caching of
457+ the pants bootstrap directories.
458+
459+ (Added in bootstrap version 1.)
460+
461+ bootstrap-version
462+ Print a version number for the bootstrap script itself.
463+
464+ Distributed scripts (such as reusable CI formulae) that use these bootstrap
465+ tools should set PANTS_BOOTSTRAP_TOOLS to the minimum script version for the
466+ features they require. For example, if 'some-tool' was added in version 123:
467+
468+ PANTS_BOOTSTRAP_TOOLS=123 ./pants some-tool
469+
470+ (Added in bootstrap version 1.)
471+ EOF
472+ ;;
473+ * )
474+ die " Unknown subcommand for bootstrap tools: $1 . Do you mean to run without PANTS_BOOTSTRAP_TOOLS=1? Or, update this script ($INSTALL_URL )?"
475+ esac
476+ }
477+
478+ if [[ " ${PANTS_BOOTSTRAP_TOOLS:- } " -gt 0 ]]; then
479+ run_bootstrap_tools " $@ "
480+ exit 0
481+ fi
482+
374483# Ensure we operate from the context of the ./pants buildroot.
375484cd " $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd -P) "
376485pants_version=" $( determine_pants_version) "
377486python=" $( determine_python_exe " ${pants_version} " ) "
378- pants_dir=" $( bootstrap_pants " ${pants_version} " " ${python} " " ${PANTS_SHA:- } " ) " || exit 1
487+ pants_dir=" $( bootstrap_pants " ${pants_version} " " ${python} " " ${PANTS_SHA:- } " " ${PANTS_DEBUG :- } " ) " || exit 1
379488
380489pants_python=" ${pants_dir} /bin/python"
381- pants_binary=" ${pants_dir} /bin/pants"
490+ pants_binary=( ${pants_dir} /bin/pants)
382491pants_extra_args=" "
383492if [[ -n " ${PANTS_SHA:- } " ]]; then
384493 pants_extra_args=" ${pants_extra_args} --python-repos-repos=$( find_links_url " $pants_version " " $PANTS_SHA " ) "
385494fi
386495
496+ pants_debug_args=()
497+ if [ -n " ${PANTS_DEBUG:- } " ]; then
498+ if [[ " $* " != * " --no-pantsd" * ]]; then
499+ echo " Error! Must pass '--no-pantsd' when using PANTS_DEBUG"
500+ exit 1
501+ fi
502+ # NB: We can't invoke `-m debugpy` as that'll prepend CWD to sys.path which might have unintended side-effects.
503+ # `-c` also prepends, but we strip that ourselves.
504+ pants_binary=(-c " __import__(\" sys\" ).path.pop(0);__import__(\" debugpy.server.cli\" ).server.cli.main()" --listen 127.0.0.1:5678 --wait-for-client " ${pants_binary[@]} " )
505+ echo " Will launch debugpy server at '127.0.0.1:5678' waiting for client connection."
506+ fi
507+
387508# shellcheck disable=SC2086
388- exec " ${pants_python} " " ${pants_binary} " ${pants_extra_args} \
509+ exec " ${pants_python} " " ${pants_binary[@] } " ${pants_extra_args} \
389510 --pants-bin-name=" ${PANTS_BIN_NAME} " --pants-version=${pants_version} " $@ "
0 commit comments