mirror of
https://github.com/pyenv/pyenv.git
synced 2026-09-10 07:36:16 -04:00
pyenv-binary: add the build subcommand
Packaging a version by hand means running `pyenv install`, `pyenv binary save` and `pyenv binary generate-installer` in turn and keeping the names they produce in step. `build` runs the three in one pass, so the archive, its metadata and the definition all come out named after the entry. * The entry name is given explicitly as `<version>:<entry>`, reusing the syntax `pyenv install` already accepts, so a packaged build can sit beside a plain source install of the same version * Entry names that `pyenv install` would not install under (`latest`), or that would point outside versions/, are refused before anything is compiled rather than after * Linux only for now, matching `save` and `generate-installer`
This commit is contained in:
parent
bec9a562a5
commit
094c0a80ed
|
|
@ -16,6 +16,22 @@ and dependency metadata exist to catch that.
|
|||
|
||||
## Commands
|
||||
|
||||
### `pyenv binary build <version>:<entry> --archive-url <url>`
|
||||
|
||||
Builds `<version>` from source under the separate name `<entry>`, packages it
|
||||
with `save`, then emits a python-build definition for it with
|
||||
`generate-installer`. The archive, metadata and definition land in the current
|
||||
directory, named after the entry; host the archive under `<url>` and drop the
|
||||
definition into python-build's definition directory. Keeping the entry name
|
||||
distinct from the version lets the binary sit alongside a normal source
|
||||
install of the same version.
|
||||
|
||||
```sh
|
||||
pyenv binary build 3.12.7:3.12.7-debian-12 --archive-url https://example.com/binaries
|
||||
# writes 3.12.7-debian-12-linux-x86_64.tar.gz, its .meta file and
|
||||
# a `3.12.7-debian-12' definition
|
||||
```
|
||||
|
||||
### `pyenv binary save <version> [<output-dir>]`
|
||||
|
||||
Packs an installed version into `<version>-<platform>.tar.gz` (relative paths)
|
||||
|
|
|
|||
83
plugins/pyenv-binary/libexec/pyenv-binary-build
Executable file
83
plugins/pyenv-binary/libexec/pyenv-binary-build
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Summary: Build a Python version and package it as an installable binary
|
||||
#
|
||||
# Usage: pyenv binary build <version>:<entry> --archive-url <url>
|
||||
#
|
||||
# Builds <version> from source under the separate name <entry>, packages the
|
||||
# result with `pyenv binary save', then emits a python-build definition for
|
||||
# it with `pyenv binary generate-installer'. The archive, metadata and
|
||||
# definition are written to the current directory, named after <entry>.
|
||||
#
|
||||
# <version> A version `pyenv install' knows how to build.
|
||||
# <entry> The name to build under and to install the binary
|
||||
# as, e.g. `3.13.14-debian-12'. Keeping it distinct
|
||||
# from <version> lets the binary sit alongside a
|
||||
# normal source install of the same version.
|
||||
# --archive-url <url> Where the archive will be hosted; the definition
|
||||
# downloads it from <url>/<archive-name>.
|
||||
#
|
||||
set -e
|
||||
[ -n "$PYENV_DEBUG" ] && set -x
|
||||
|
||||
# Provide pyenv completions. Only the definitions are useful here: the options
|
||||
# `pyenv install' takes are not accepted.
|
||||
if [ "$1" = "--complete" ]; then
|
||||
echo --archive-url
|
||||
exec python-build --definitions
|
||||
fi
|
||||
|
||||
spec=""
|
||||
archive_url=""
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--archive-url )
|
||||
[ $# -ge 2 ] || { echo "pyenv-binary: --archive-url needs a value" >&2; exit 1; }
|
||||
archive_url="$2"; shift 2 ;;
|
||||
-* )
|
||||
echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;;
|
||||
* )
|
||||
[ -z "$spec" ] || { echo "pyenv-binary: unexpected argument \`$1'" >&2; exit 1; }
|
||||
spec="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$spec" ] || [ -z "$archive_url" ]; then
|
||||
pyenv-help --usage binary-build >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$spec" in
|
||||
*?:?* ) ;;
|
||||
* )
|
||||
echo "pyenv-binary: expected <version>:<entry>, e.g. \`3.13.14:3.13.14-debian-12'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
entry="${spec##*:}"
|
||||
|
||||
case "$entry" in
|
||||
# `pyenv install' reads a trailing `:latest' as part of the version rather than
|
||||
# as an alias, so nothing would end up installed under that name.
|
||||
latest )
|
||||
echo "pyenv-binary: \`latest' cannot be used as an entry name" >&2
|
||||
exit 1
|
||||
;;
|
||||
# The entry becomes a directory name under versions/. `pyenv install' does not
|
||||
# validate the alias, and `save' only checks once the build is done, so refuse
|
||||
# a name that could point elsewhere before compiling anything.
|
||||
*/* | .. | . )
|
||||
echo "pyenv-binary: invalid entry name \`${entry}'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# `pyenv install' puts a `<version>:<alias>' build under versions/<alias>.
|
||||
pyenv-install "$spec"
|
||||
pyenv-binary-save "$entry"
|
||||
|
||||
# Name the metadata and the archive the way `save' just named them.
|
||||
platform="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
|
||||
pyenv-binary-generate-installer "${entry}-${platform}.meta" \
|
||||
--archive-url "${archive_url%/}/${entry}-${platform}.tar.gz" -o "$entry"
|
||||
79
plugins/pyenv-binary/test/build.bats
Normal file
79
plugins/pyenv-binary/test/build.bats
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
# Make the build deterministic: `pyenv-install' just creates the prefix, and
|
||||
# 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 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 the buildable versions" {
|
||||
create_stub python-build 'echo 3.12.7'
|
||||
run pyenv-binary-build --complete
|
||||
assert_success "--archive-url
|
||||
3.12.7"
|
||||
}
|
||||
|
||||
@test "fails with no arguments" {
|
||||
create_stub pyenv-help "echo usage"
|
||||
run pyenv-binary-build
|
||||
assert_failure "usage"
|
||||
}
|
||||
|
||||
@test "fails without an archive url" {
|
||||
create_stub pyenv-help "echo usage"
|
||||
run pyenv-binary-build 3.12.7:3.12.7-test
|
||||
assert_failure "usage"
|
||||
}
|
||||
|
||||
@test "fails when --archive-url has no value" {
|
||||
run pyenv-binary-build 3.12.7:3.12.7-test --archive-url
|
||||
assert_failure "pyenv-binary: --archive-url needs a value"
|
||||
}
|
||||
|
||||
@test "rejects a second positional argument" {
|
||||
run pyenv-binary-build 3.12.7:3.12.7-test extra --archive-url http://x/b
|
||||
assert_failure "pyenv-binary: unexpected argument \`extra'"
|
||||
}
|
||||
|
||||
@test "rejects a bare version without an entry name" {
|
||||
run pyenv-binary-build 3.12.7 --archive-url http://x/b
|
||||
assert_failure "pyenv-binary: expected <version>:<entry>, e.g. \`3.13.14:3.13.14-debian-12'"
|
||||
}
|
||||
|
||||
@test "rejects an entry name containing a slash" {
|
||||
run pyenv-binary-build "3.12.7:foo/bar" --archive-url http://x/b
|
||||
assert_failure "pyenv-binary: invalid entry name \`foo/bar'"
|
||||
}
|
||||
|
||||
@test "rejects \`latest' as an entry name" {
|
||||
run pyenv-binary-build 3.12:latest --archive-url http://x/b
|
||||
assert_failure "pyenv-binary: \`latest' cannot be used as an entry name"
|
||||
}
|
||||
|
||||
@test "builds under the entry name and writes the archive and definition" {
|
||||
stub_build_environment
|
||||
cd "${BATS_TEST_TMPDIR}"
|
||||
|
||||
run pyenv-binary-build 3.12.7:3.12.7-test --archive-url http://example.com/binaries
|
||||
assert_success
|
||||
assert [ -d "${PYENV_ROOT}/versions/3.12.7-test" ]
|
||||
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-test-linux-x86_64.tar.gz" ]
|
||||
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-test-linux-x86_64.meta" ]
|
||||
run grep '^ARCHIVE_URL=' "${BATS_TEST_TMPDIR}/3.12.7-test"
|
||||
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-test-linux-x86_64.tar.gz"
|
||||
}
|
||||
|
||||
@test "strips a trailing slash from the archive url" {
|
||||
stub_build_environment
|
||||
cd "${BATS_TEST_TMPDIR}"
|
||||
|
||||
run pyenv-binary-build 3.12.7:3.12.7-test --archive-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-linux-x86_64.tar.gz"
|
||||
}
|
||||
Loading…
Reference in a new issue