pyenv: Install a Python and the Build Deps CI Needs
pyenv install builds CPython from source, so a CI image missing libssl-dev/zlib headers fails the build; install the documented build deps first.
pyenv compiles each Python version rather than downloading a prebuilt binary. That makes it portable but means the runner needs a C toolchain and several -dev packages, the absence of which is the classic pyenv CI failure.
What it does
pyenv manages multiple Python versions via shims. pyenv install <v> downloads the CPython source and compiles it under ~/.pyenv/versions. pyenv global/pyenv local write the active version, and shims on PATH dispatch python to it.
Common usage
# Debian/Ubuntu build deps (the part CI forgets)
sudo apt-get update && sudo apt-get install -y \
make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev libffi-dev liblzma-dev
export PYENV_ROOT="$HOME/.pyenv"; export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
pyenv install 3.12.3
pyenv global 3.12.3
python --versionOptions
| Command / flag | What it does |
|---|---|
| pyenv install <v> | Compile and install a Python version |
| pyenv install -s <v> | Skip if already installed (safe in CI) |
| pyenv global <v> | Set the default version |
| pyenv local <v> | Write .python-version for this directory |
| eval "$(pyenv init -)" | Wire up shims and PATH in the shell |
| PYTHON_CONFIGURE_OPTS | Pass flags like --enable-shared to the build |
In CI
Run eval "$(pyenv init -)" (and pyenv init --path) in each step so shims are on PATH, since PATH does not persist across steps. Cache ~/.pyenv/versions to skip recompiling. Use pyenv install -s so a re-run does not rebuild an existing version.
Common errors in CI
"ModuleNotFoundError: No module named '_ssl'" or "The Python ssl extension was not compiled. Missing the OpenSSL lib?" means libssl-dev was absent at build time; install it and rebuild. "zipimport.ZipImportError: can't decompress data; zlib not available" means zlib1g-dev was missing. "pyenv: python: command not found" with a shim present usually means pyenv rehash is needed or init was not eval'd.