diff --git a/python/deploy b/python/deploy index dcaee41..f3b6f07 100755 --- a/python/deploy +++ b/python/deploy @@ -268,6 +268,21 @@ elif [ -f "${CURRENT_DIR}/uv.lock" ]; then # UV_INDEX_URL and UV_EXTRA_INDEX_URL are natively supported by uv uv sync --no-dev --no-install-project --python-preference only-system + + # uv installs dependencies into a virtualenv (default: .venv in the project + # dir, or $UV_PROJECT_ENVIRONMENT). Prepend its bin dir to PATH so the + # installed packages and console scripts are available at runtime. + VENV_PATH="${UV_PROJECT_ENVIRONMENT:-${CURRENT_DIR}/.venv}" + if [ -d "${VENV_PATH}/bin" ]; then + echo "Adding uv virtualenv to PATH: ${VENV_PATH}/bin" + # deploy runs as the (non-root) application user, so the user profile is + # written directly while the system profile needs sudo (see buildpack/deploy). + echo "export PATH=${VENV_PATH}/bin:\${PATH}" | tee -a ${HOME}/.profile >/dev/null + echo "export PATH=${VENV_PATH}/bin:\${PATH}" | sudo tee -a /etc/profile >/dev/null + else + echo "WARNING: uv virtualenv not found at ${VENV_PATH}, PATH not updated" + fi + elif [ -f "${CURRENT_DIR}/Pipfile.lock" ]; then echo "Pipfile.lock detected, using 'pipenv install --system --deploy' to install dependencies" diff --git a/tests/python/tests.bats b/tests/python/tests.bats index 68d28f0..f6d9324 100644 --- a/tests/python/tests.bats +++ b/tests/python/tests.bats @@ -497,6 +497,68 @@ EOF rm -rf ${CURRENT_DIR}/pyproject.toml ${CURRENT_DIR}/uv.lock ${CURRENT_DIR}/.venv } +@test "uv.lock adds virtualenv bin to PATH profile" { + unset PYTHON_VERSION + cp uv_pyproject.toml ${CURRENT_DIR}/pyproject.toml + cp uv.lock ${CURRENT_DIR}/ + + # Start from clean profile files so we can assert exactly what gets appended + # (/etc/profile is root-owned, deploy writes to it via sudo) + rm -f ${HOME}/.profile + sudo rm -f /etc/profile + + run /var/lib/tsuru/deploy + assert_success + + [[ "$output" == *"uv.lock detected"* ]] + [[ "$output" == *"Adding uv virtualenv to PATH: ${CURRENT_DIR}/.venv/bin"* ]] + + # The venv bin dir must be exported in both the user and system profiles + run grep -F "export PATH=${CURRENT_DIR}/.venv/bin:\${PATH}" ${HOME}/.profile + assert_success + run grep -F "export PATH=${CURRENT_DIR}/.venv/bin:\${PATH}" /etc/profile + assert_success + + # A fresh login shell that sources the profile must expose the venv binaries + run bash -lc 'command -v python' + assert_success + [[ "$output" == *"${CURRENT_DIR}/.venv/bin/python"* ]] + + rm -rf ${CURRENT_DIR}/pyproject.toml ${CURRENT_DIR}/uv.lock ${CURRENT_DIR}/.venv + rm -f ${HOME}/.profile + sudo rm -f /etc/profile +} + +@test "uv.lock respects UV_PROJECT_ENVIRONMENT for PATH profile" { + unset PYTHON_VERSION + export UV_PROJECT_ENVIRONMENT=${CURRENT_DIR}/custom-venv + cp uv_pyproject.toml ${CURRENT_DIR}/pyproject.toml + cp uv.lock ${CURRENT_DIR}/ + + rm -f ${HOME}/.profile + sudo rm -f /etc/profile + + run /var/lib/tsuru/deploy + assert_success + + [[ "$output" == *"uv.lock detected"* ]] + [[ "$output" == *"Adding uv virtualenv to PATH: ${CURRENT_DIR}/custom-venv/bin"* ]] + + run grep -F "export PATH=${CURRENT_DIR}/custom-venv/bin:\${PATH}" ${HOME}/.profile + assert_success + + pushd ${CURRENT_DIR} + run uv pip freeze --python custom-venv/bin/python + popd + assert_success + [[ "$output" == *"msgpack"* ]] + + rm -rf ${CURRENT_DIR}/pyproject.toml ${CURRENT_DIR}/uv.lock ${CURRENT_DIR}/custom-venv + rm -f ${HOME}/.profile + sudo rm -f /etc/profile + unset UV_PROJECT_ENVIRONMENT +} + @test "install from uv.lock with custom uv version" { unset PYTHON_VERSION export PYTHON_UV_VERSION=0.7.0