ci: make python setup resilient
All checks were successful
CI/CD Pipeline / Manual Action Help (push) Has been skipped
CI/CD Pipeline / Start Dev Containers in Dokploy (push) Has been skipped
CI/CD Pipeline / Drop and Recreate Dev Database (push) Has been skipped
CI/CD Pipeline / Quality Gate (push) Successful in 8m16s
CI/CD Pipeline / Build and Push Images (push) Successful in 1m36s
CI/CD Pipeline / Internal Notify (push) Successful in 1s
CI/CD Pipeline / Deploy Dev in Dokploy (push) Successful in 1s

This commit is contained in:
2026-04-28 14:22:19 +02:00
parent 1331a5bbd1
commit d823e11f19

View File

@@ -11,10 +11,52 @@ else
PYTHON_BIN="python${PYTHON_MAJOR}"
fi
if command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
command -v "${PYTHON_BIN}"
python_matches() {
local candidate="$1"
[[ -x "${candidate}" ]] || return 1
"${candidate}" - "${PYTHON_MAJOR}" "${PYTHON_MINOR:-}" <<'PY'
import sys
expected_major = int(sys.argv[1])
expected_minor = sys.argv[2]
if sys.version_info.major != expected_major:
raise SystemExit(1)
if expected_minor and sys.version_info.minor != int(expected_minor):
raise SystemExit(1)
PY
}
python_has_venv() {
local candidate="$1"
"${candidate}" -m venv --help >/dev/null 2>&1
}
print_if_usable() {
local candidate="$1"
if python_matches "${candidate}" && python_has_venv "${candidate}"; then
printf '%s\n' "${candidate}"
exit 0
fi
}
if command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
print_if_usable "$(command -v "${PYTHON_BIN}")"
fi
TOOLCACHE_DIR="${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}"
HOME_DIR="${HOME:-/root}"
shopt -s nullglob
# shellcheck disable=SC2140
for candidate in \
"${TOOLCACHE_DIR}"/Python/"${PYTHON_MAJOR}.${PYTHON_MINOR}"*/x64/bin/python \
"${TOOLCACHE_DIR}"/Python/"${PYTHON_MAJOR}.${PYTHON_MINOR}"*/x64/bin/python3 \
"${TOOLCACHE_DIR}"/uv-python/cpython-"${PYTHON_MAJOR}.${PYTHON_MINOR}"*/bin/python \
"${HOME_DIR}"/.local/share/uv/python/cpython-"${PYTHON_MAJOR}.${PYTHON_MINOR}"*/bin/python \
"${HOME_DIR}"/.cache/uv/python/cpython-"${PYTHON_MAJOR}.${PYTHON_MINOR}"*/bin/python; do
print_if_usable "${candidate}"
done
shopt -u nullglob
APT_RUNNER=()
if [[ "$(id -u)" -ne 0 ]]; then
@@ -23,12 +65,42 @@ fi
export DEBIAN_FRONTEND=noninteractive
"${APT_RUNNER[@]}" apt-get update >&2
if ! "${APT_RUNNER[@]}" apt-get install -y "${PYTHON_BIN}" "${PYTHON_BIN}-venv" >&2; then
"${APT_RUNNER[@]}" apt-get install -y software-properties-common >&2
"${APT_RUNNER[@]}" add-apt-repository -y ppa:deadsnakes/ppa >&2
"${APT_RUNNER[@]}" apt-get update >&2
"${APT_RUNNER[@]}" apt-get install -y "${PYTHON_BIN}" "${PYTHON_BIN}-venv" >&2
if "${APT_RUNNER[@]}" apt-get update >&2 \
&& "${APT_RUNNER[@]}" apt-get install -y "${PYTHON_BIN}" "${PYTHON_BIN}-venv" >&2; then
print_if_usable "$(command -v "${PYTHON_BIN}")"
fi
command -v "${PYTHON_BIN}"
install_uv_standalone() {
if command -v uv >/dev/null 2>&1; then
command -v uv
return 0
fi
local uv_dir
uv_dir="$(mktemp -d)"
local install_url="https://astral.sh/uv/${UV_VERSION:-0.7.2}/install.sh"
if ! curl -LsSf "${install_url}" | env UV_INSTALL_DIR="${uv_dir}/bin" sh >&2; then
curl -LsSf "https://astral.sh/uv/install.sh" | env UV_INSTALL_DIR="${uv_dir}/bin" sh >&2
fi
printf '%s\n' "${uv_dir}/bin/uv"
}
UV_BIN="$(install_uv_standalone)"
UV_PYTHON_INSTALL_DIR="${TOOLCACHE_DIR:-${HOME_DIR}/.cache}/uv-python" \
"${UV_BIN}" python install "${TARGET_VERSION}" --no-progress >&2
UV_PYTHON_CANDIDATE="$(
UV_PYTHON_INSTALL_DIR="${TOOLCACHE_DIR:-${HOME_DIR}/.cache}/uv-python" \
"${UV_BIN}" python find "${TARGET_VERSION}" --managed-python
)"
print_if_usable "${UV_PYTHON_CANDIDATE}"
if "${APT_RUNNER[@]}" apt-get install -y software-properties-common >&2 \
&& "${APT_RUNNER[@]}" add-apt-repository -y ppa:deadsnakes/ppa >&2 \
&& "${APT_RUNNER[@]}" apt-get update >&2 \
&& "${APT_RUNNER[@]}" apt-get install -y "${PYTHON_BIN}" "${PYTHON_BIN}-venv" >&2; then
print_if_usable "$(command -v "${PYTHON_BIN}")"
fi
printf 'Unable to install a usable Python %s with venv support\n' "${TARGET_VERSION}" >&2
exit 1