Files
mostovik-backend/scripts/ensure-ci-python.sh
Aleksandr Meshchriakov ab08402af9
Some checks failed
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 / Quality Gate (push) Failing after 2s
CI/CD Pipeline / Drop and Recreate Dev Database (push) Has been skipped
CI/CD Pipeline / Build and Push Images (push) Has been skipped
CI/CD Pipeline / Deploy Dev in Dokploy (push) Has been skipped
CI/CD Pipeline / Internal Notify (push) Successful in 1s
ci: ignore stale project venv in runner
2026-04-28 16:22:53 +02:00

112 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
TARGET_VERSION="${1:-3.11}"
IFS="." read -r PYTHON_MAJOR PYTHON_MINOR _ <<< "${TARGET_VERSION}"
if [[ -n "${PYTHON_MINOR:-}" ]]; then
PYTHON_BIN="python${PYTHON_MAJOR}.${PYTHON_MINOR}"
else
PYTHON_BIN="python${PYTHON_MAJOR}"
fi
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"
case "${candidate}" in
"${PWD}/.venv/"*)
return 1
;;
esac
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
APT_RUNNER=(sudo)
fi
export DEBIAN_FRONTEND=noninteractive
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
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