Merge pull request #3516 from macayu17/fix/2334-package-feedback

pyenv-binary-package: check patchelf early and add --verbose
This commit is contained in:
native-api 2026-08-11 05:25:39 +03:00 committed by GitHub
commit d92b6e1bbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 122 additions and 45 deletions

View file

@ -16,7 +16,7 @@ and dependency metadata exist to catch that.
## Commands
### `pyenv binary package <version>[:<entry>] --archive-base-url <url>`
### `pyenv binary package [-v|--verbose] <version>[:<entry>] --archive-base-url <url>`
Installs `<version>` 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

View file

@ -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"
}

View file

@ -2,7 +2,7 @@
#
# Summary: Create an installable binary package from a Python version
#
# Usage: pyenv binary package <version>[:<entry>] --archive-base-url <url>
# Usage: pyenv binary package [-v|--verbose] <version>[:<entry>] --archive-base-url <url>
#
# Installs <version> 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 @@
# <entry> 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 <url>
# Where the archive will be hosted; the definition
# downloads it from <url>/<entry>.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 `<version>:<alias>' build under versions/<alias>.
pyenv-install "$spec"
pyenv-install ${verbose:+--verbose} "$spec"
pyenv-binary-save "$entry" "$PWD" --name "$entry"
pyenv-binary-generate-installer "${entry}.meta" \

View file

@ -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 \

View file

@ -6,22 +6,33 @@ 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 "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 "-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 "completions" {
create_stub pyenv-install 'echo "${0##*/} $*"'
run pyenv-binary-package --complete
assert_success
assert_line "--archive-base-url"
assert_line "3.12.7-example"
refute_line "Available versions:"
assert_success <<!
--archive-base-url
--verbose
pyenv-install --list --bare
!
}
@test "fails with no arguments" {
@ -76,7 +87,7 @@ stub_build_environment() {
assert_failure "pyenv-binary: macOS archives are not supported yet"
}
@test "writes the archive, metadata and definition under the entry name" {
@test "writes the archive, metadata and definition under the entry name (integration)" {
stub_build_environment
cd "${BATS_TEST_TMPDIR}"
@ -90,13 +101,24 @@ stub_build_environment() {
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-test.tar.gz"
}
@test "strips a trailing slash from the archive base url" {
@test "correctly joins archive base url with a trailing slash" {
stub_build_environment
cd "${BATS_TEST_TMPDIR}"
create_stub pyenv-binary-save true
create_stub pyenv-binary-generate-installer <<'!'
echo -n "${0##*/} "
while (($#)); do
case $1 in
--archive-url)
echo "$1 ${2:?}"
break
;;
esac
shift
done
!
run pyenv-binary-package 3.12.7:3.12.7-test \
--archive-base-url http://example.com/binaries/
assert_success
run grep '^ARCHIVE_URL=' "${BATS_TEST_TMPDIR}/3.12.7-test"
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-test.tar.gz"
assert_line "pyenv-binary-generate-installer --archive-url http://example.com/binaries/3.12.7-test.tar.gz"
}

View file

@ -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 <<!
assert_failure
assert_output_glob <<!
pyenv: cannot rehash: couldn't acquire lock ${PYENV_ROOT}/shims/.pyenv-shim for 1 seconds. Last error message:
*/pyenv-rehash: line *: ${PYENV_ROOT}/shims/.pyenv-shim: cannot overwrite existing file
!

View file

@ -58,34 +58,64 @@ assert_success() {
if [ "$status" -ne 0 ]; then
flunk "command failed with exit status $status" $'\n'\
"output: $output"
elif [ "$#" -gt 0 ]; then
assert_output "$1"
fi
_assert_output_if_provided "$@"
}
assert_failure() {
if [ "$status" -eq 0 ]; then
flunk "expected failed exit status" $'\n'\
"output: $output"
elif [ "$#" -gt 0 ]; then
assert_output "$1"
fi
_assert_output_if_provided "$@"
}
assert_equal() {
if [ "$1" != "$2" ]; then
{ echo "expected: \`$1'"
echo "actual: \`$2'"
_assert_equal_as text "$@"
}
_assert_equal_as() {
if case "$1" in
text)
[[ "$2" != "$3" ]];;
glob)
[[ "$3" != $2 ]];;
*)
echo "invalid comparison type: \`$1'" >&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() {

View file

@ -90,13 +90,13 @@ IN
}
@test "skips glob path traversal" {
cat > my-version <<IN
cat > my-version <<'IN'
../*
3.9.3
IN
run pyenv-version-file-read my-version
assert_success <<OUT
pyenv: invalid version \`../\*' ignored in \`my-version'
assert_success <<'OUT'
pyenv: invalid version `../*' ignored in `my-version'
3.9.3
OUT
}

View file

@ -83,8 +83,7 @@ OUT
create_version "3.4.0/envs/bar"
create_version "3.5.2"
run pyenv-versions
assert_success
assert_output <<OUT
assert_success <<OUT
* system (set by ${PYENV_ROOT}/version)
2.7.6
3.4.0
@ -95,6 +94,7 @@ OUT
}
@test "skips envs with --skip-envs" {
stub_system_python
create_version "3.3.3"
create_version "3.4.0"
create_version "3.4.0/envs/foo"
@ -102,7 +102,7 @@ OUT
create_version "3.5.0"
run pyenv-versions --skip-envs
assert_success <<OUT
assert_success <<OUT
* system (set by ${PYENV_ROOT}/version)
3.3.3
3.4.0
@ -188,9 +188,7 @@ OUT
create_external_version "moo"
run pyenv-versions --bare --skip-aliases
assert_success
assert_output <<OUT
assert_success <<OUT
1.8.7
moo
OUT

View file

@ -64,7 +64,7 @@ load test_helper
assert_failure <<OUT
pyenv: version \`3.3' is not installed (set by PYENV_VERSION environment variable)
pyenv: py.test: command not found
The \`py.test' command exists in these Python versions:
3.4
@ -81,7 +81,7 @@ OUT
pyenv: version \`2.7' is not installed (set by PYENV_VERSION environment variable)
pyenv: version \`3.3' is not installed (set by PYENV_VERSION environment variable)
pyenv: py.test: command not found
The \`py.test' command exists in these Python versions:
3.4