From 2c1f405180f31e54d8c1fc0d11614e3de3189ae1 Mon Sep 17 00:00:00 2001 From: macayu17 Date: Mon, 10 Aug 2026 06:52:54 +0300 Subject: [PATCH 1/5] pyenv-binary: check patchelf before installing archive --- .../libexec/pyenv-binary-generate-installer | 5 +++++ plugins/pyenv-binary/test/generate-installer.bats | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/plugins/pyenv-binary/libexec/pyenv-binary-generate-installer b/plugins/pyenv-binary/libexec/pyenv-binary-generate-installer index b0282a56..4e943ba8 100755 --- a/plugins/pyenv-binary/libexec/pyenv-binary-generate-installer +++ b/plugins/pyenv-binary/libexec/pyenv-binary-generate-installer @@ -185,6 +185,11 @@ elif [ -n "$DEPS" ]; then echo "pyenv-binary: cannot check required system libraries (ldconfig cache unavailable)" >&2 fi +if ! command -v patchelf >/dev/null 2>&1; then + echo "pyenv-binary: need patchelf to relocate the binary" >&2 + exit 1 +fi + build_package_relocate() { pyenv binary relocate "$PREFIX_PATH" } diff --git a/plugins/pyenv-binary/test/generate-installer.bats b/plugins/pyenv-binary/test/generate-installer.bats index 8f098c51..02f63fac 100644 --- a/plugins/pyenv-binary/test/generate-installer.bats +++ b/plugins/pyenv-binary/test/generate-installer.bats @@ -106,6 +106,17 @@ create_meta() { assert_failure "pyenv-binary: missing required system libraries: libmissing.so.1" } +@test "checks for patchelf before installing the archive" { + local out="${BATS_TEST_TMPDIR}/definition" + pyenv-binary-generate-installer "$(create_meta)" \ + --archive-url http://example.com/a.tar.gz -o "$out" + create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac' + create_stub getconf 'echo "glibc 2.31"' + + PATH="$(path_without patchelf)" run bash "$out" + assert_failure "pyenv-binary: need patchelf to relocate the binary" +} + @test "fails when the archive is not beside the metadata" { local meta="$(create_meta)" rm "${BATS_TEST_TMPDIR}/3.12.7.tar.gz" @@ -125,6 +136,7 @@ create_meta() { mkdir -p "$cache" cp "$archive" "${cache}/Python-3.12.7-binary.tar.gz" create_stub pyenv 'echo "pyenv $*"' + create_path_executable patchelf "exit 0" create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac' PYTHON_BUILD_CACHE_PATH="$cache" run \ From 4a73bd17e2291e12fbc6cb8959f466a05c9853d1 Mon Sep 17 00:00:00 2001 From: macayu17 Date: Mon, 10 Aug 2026 06:52:55 +0300 Subject: [PATCH 2/5] pyenv-binary-package: add --verbose Co-authored-by: Ivan Pozdeev --- plugins/pyenv-binary/README.md | 4 +++- .../pyenv-binary/libexec/pyenv-binary-package | 17 ++++++++++++----- plugins/pyenv-binary/test/package.bats | 18 +++++++++++++++--- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/plugins/pyenv-binary/README.md b/plugins/pyenv-binary/README.md index 5e3ba20a..817e9a6f 100644 --- a/plugins/pyenv-binary/README.md +++ b/plugins/pyenv-binary/README.md @@ -16,7 +16,7 @@ and dependency metadata exist to catch that. ## Commands -### `pyenv binary package [:] --archive-base-url ` +### `pyenv binary package [-v|--verbose] [:] --archive-base-url ` Installs `` from source under a separate name, packages that install with `save`, then emits a python-build definition for it with @@ -24,6 +24,8 @@ with `save`, then emits a python-build definition for it with current platform, platform version and architecture. An explicit entry keeps the existing custom-build workflow. +Pass `-v` to show build progress from `pyenv install`. + ```sh pyenv binary package 3.12.7 \ --archive-base-url https://example.com/binaries diff --git a/plugins/pyenv-binary/libexec/pyenv-binary-package b/plugins/pyenv-binary/libexec/pyenv-binary-package index 88a0283f..4c6592bc 100755 --- a/plugins/pyenv-binary/libexec/pyenv-binary-package +++ b/plugins/pyenv-binary/libexec/pyenv-binary-package @@ -2,7 +2,7 @@ # # Summary: Create an installable binary package from a Python version # -# Usage: pyenv binary package [:] --archive-base-url +# Usage: pyenv binary package [-v|--verbose] [:] --archive-base-url # # Installs from source under a separate entry name, saves it as a # binary package, then emits a python-build definition for that package. @@ -13,6 +13,7 @@ # The optional name to build under and install the # binary as. If omitted, a name is generated from the # current platform, platform version and architecture. +# -v,--verbose Show build progress from `pyenv install'. # --archive-base-url # Where the archive will be hosted; the definition # downloads it from /.tar.gz. @@ -23,23 +24,29 @@ set -e # Provide pyenv completions if [ "$1" = "--complete" ]; then echo --archive-base-url + echo --verbose exec pyenv-install --list --bare fi spec="" archive_base_url="" +verbose="" while [ $# -gt 0 ]; do case "$1" in --archive-base-url ) - [ $# -ge 2 ] || { echo "pyenv-binary: --archive-base-url needs a value" >&2; exit 1; } - archive_base_url="$2"; shift 2 ;; + [ $# -lt 2 ] && { echo "pyenv-binary: --archive-base-url needs a value" >&2; exit 1; } + archive_base_url="$2"; shift ;; + -v|--verbose) + verbose=1 ;; -* ) echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;; * ) [ -z "$spec" ] || { echo "pyenv-binary: unexpected argument \`$1'" >&2; exit 1; } - spec="$1"; shift ;; + spec="$1" + ;; esac + shift done if [ -z "$spec" ] || [ -z "$archive_base_url" ]; then @@ -78,7 +85,7 @@ if [ "$os" = "Darwin" ]; then fi # `pyenv install' puts a `:' build under versions/. -pyenv-install "$spec" +pyenv-install ${verbose:+--verbose} "$spec" pyenv-binary-save "$entry" "$PWD" --name "$entry" pyenv-binary-generate-installer "${entry}.meta" \ diff --git a/plugins/pyenv-binary/test/package.bats b/plugins/pyenv-binary/test/package.bats index 6262a8c3..b3f2f6dc 100644 --- a/plugins/pyenv-binary/test/package.bats +++ b/plugins/pyenv-binary/test/package.bats @@ -6,12 +6,24 @@ load test_helper # the platform tools report a fixed Linux target so the real `save' and # `generate-installer' behave the same on any test host. stub_build_environment() { - create_stub pyenv-install 'mkdir -p "${PYENV_ROOT}/versions/${1##*:}/bin"' - create_stub pyenv-latest '[ "$1" = "-f" ] && [ "$2" = "-k" ] && shift 2 && echo "$*"' + create_stub pyenv-install 'echo "${0##*/} $*"; mkdir -p "${PYENV_ROOT}/versions/${1##*:}/bin"' + create_stub pyenv-latest 'while (($#)); do case "$1" in -f|-k);; *)break;; esac; shift; done; echo "$*"' create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac' create_stub getconf 'echo "glibc 2.17"' } +@test "-v|--verbose runs pyenv install verbosely" { + stub_build_environment + create_stub pyenv-binary-save true + create_stub pyenv-binary-generate-installer true + + for opt in "" -v --verbose; do + run pyenv-binary-package $opt 3.12.7:3.12.7-test \ + --archive-base-url http://example.com/binaries + assert_success "pyenv-install ${opt:+--verbose }3.12.7:3.12.7-test" + done +} + @test "completion lists the option and definitions provided by another plugin" { mkdir -p "${PYENV_ROOT}/plugins/example/share/python-build" touch "${PYENV_ROOT}/plugins/example/share/python-build/3.12.7-example" @@ -20,6 +32,7 @@ stub_build_environment() { run pyenv-binary-package --complete assert_success assert_line "--archive-base-url" + assert_line "--verbose" assert_line "3.12.7-example" refute_line "Available versions:" } @@ -92,7 +105,6 @@ stub_build_environment() { @test "strips a trailing slash from the archive base url" { stub_build_environment - cd "${BATS_TEST_TMPDIR}" run pyenv-binary-package 3.12.7:3.12.7-test \ --archive-base-url http://example.com/binaries/ From bd7c969e7f50b6128023b186c99203dfead6deb4 Mon Sep 17 00:00:00 2001 From: Ivan Pozdeev Date: Tue, 11 Aug 2026 01:51:12 +0300 Subject: [PATCH 3/5] test_helper: show diff with whitespace highlights to be able to see tiny and whitespace differences --- test/test_helper.bash | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_helper.bash b/test/test_helper.bash index 30c04629..8dd403fa 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -74,8 +74,7 @@ assert_failure() { assert_equal() { if [ "$1" != "$2" ]; then - { echo "expected: \`$1'" - echo "actual: \`$2'" + { diff -u --label expected --label actual <(echo "$1") <(echo "$2") | cat -te } | flunk fi } From f9ffb73a7ed4ea0b6d4f99dbd5b43e294d1a5f2e Mon Sep 17 00:00:00 2001 From: Ivan Pozdeev Date: Mon, 10 Aug 2026 21:19:47 +0300 Subject: [PATCH 4/5] test_helper: support here-document to `assert_success/failure`; + assert_output_glob --- test/rehash.bats | 3 ++- test/test_helper.bash | 53 +++++++++++++++++++++++++++++-------- test/version-file-read.bats | 6 ++--- test/versions.bats | 10 +++---- test/which.bats | 4 +-- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/test/rehash.bats b/test/rehash.bats index c1476e6c..342d7e0c 100755 --- a/test/rehash.bats +++ b/test/rehash.bats @@ -23,7 +23,8 @@ load test_helper touch "${PYENV_ROOT}/shims/.pyenv-shim" #avoid failure due to a localized error message LANG=C run pyenv-rehash - assert_failure <&2; return 1;; + esac + then + { diff -u --label expected --label actual <(echo "$2") <(echo "$3") | cat -te } | flunk fi } -assert_output() { - local expected - if [ $# -eq 0 ]; then expected="$(cat -)" - else expected="$1" +_assert_output_if_provided() { + if [ "$#" -gt 0 ]; then + assert_output "$1" + elif [ ! -t 0 ]; then + local expected=$(cat -) + if [[ -n $expected ]]; then + assert_output "$expected" + fi fi - assert_equal "$expected" "$output" +} + +assert_output() { + _assert_output_as text "$@" +} + +assert_output_glob() { + _assert_output_as glob "$@" +} + +_assert_output_as() { + local kind="${1:?}"; shift + local expected + if [ $# -eq 0 ] + then expected="$(cat -)" + else expected="$1" + fi + _assert_equal_as "$kind" "$expected" "$output" } assert_line() { diff --git a/test/version-file-read.bats b/test/version-file-read.bats index 6563eb6f..d20dfd2c 100644 --- a/test/version-file-read.bats +++ b/test/version-file-read.bats @@ -90,13 +90,13 @@ IN } @test "skips glob path traversal" { - cat > my-version < my-version <<'IN' ../* 3.9.3 IN run pyenv-version-file-read my-version - assert_success < Date: Mon, 10 Aug 2026 20:31:00 +0300 Subject: [PATCH 5/5] pyenv-binary-package: refactor tests * make them more human-readable * don't double-test external code outside of integration tests --- plugins/pyenv-binary/test/package.bats | 36 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/plugins/pyenv-binary/test/package.bats b/plugins/pyenv-binary/test/package.bats index b3f2f6dc..b954fa6d 100644 --- a/plugins/pyenv-binary/test/package.bats +++ b/plugins/pyenv-binary/test/package.bats @@ -24,17 +24,15 @@ stub_build_environment() { done } -@test "completion lists the option and definitions provided by another plugin" { - mkdir -p "${PYENV_ROOT}/plugins/example/share/python-build" - touch "${PYENV_ROOT}/plugins/example/share/python-build/3.12.7-example" - PATH="${BATS_TEST_DIRNAME}/../../python-build/bin:${PATH}" +@test "completions" { + create_stub pyenv-install 'echo "${0##*/} $*"' run pyenv-binary-package --complete - assert_success - assert_line "--archive-base-url" - assert_line "--verbose" - assert_line "3.12.7-example" - refute_line "Available versions:" + assert_success <