Skip to content

Commit e66cff8

Browse files
committed
Never skip running pipenv install
Previously the buildpack would skip running `pipenv install` for repeat Pipenv builds if (a) the SHA256 of the `Pipfile.lock` file had not changed since the last successful build, and (b) there were no Git VCS references in the lockfile. However, this has a few issues: 1. There are other cases where it's not safe to assume that there is no need to re-run `pipenv install`, such as when installing a non-editable local dependency (see #1525), or when using editable local dependencies with the current path re-writing strategy (see #1520). 2. The current Git VCS check has false positives (see #1130, #1398). 3. Even if we try and add more checks/workarounds to resolve (1) and (2), we're still having to make assumptions about internal Pipenv implementation details, and hardcode these in the buildpack, hoping we didn't miss anything and that Pipenv's behaviour doesn't change over time (which is not the case, as seen by the recent regression pypa/pipenv#6054) As such, we now instead always re-run `pipenv install`, and defer to Pipenv to decide whether the environment needs updating. This should still be fast, since the cached `site-packages` is still being used (and if there are any scenarios in which it's not fast, then that's an upstream Pipenv bug). Integration tests were also added for various types of editable Pipenv installs, since we previously only had test coverage of editable installs for Pip. Fixes #1520. Fixes #1525. Closes #1130. Closes #1398.
1 parent d1ca05a commit e66cff8

File tree

15 files changed

+297
-57
lines changed

15 files changed

+297
-57
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44

55
- Updated pip from 23.3.1 to 23.3.2. ([#1524](https://github.com/heroku/heroku-buildpack-python/pull/1524))
6+
- Fixed repeat/cached Pipenv builds of local `file =` dependencies. ([#1526](https://github.com/heroku/heroku-buildpack-python/pull/1526))
67

78
## [v241] - 2023-12-08
89

bin/steps/pipenv

+37-53
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,50 @@
66
source "$BIN_DIR/utils"
77
set -e
88

9-
if [[ -f Pipfile.lock ]]; then
10-
if [[ -f .heroku/python/Pipfile.lock.sha256 ]]; then
11-
if [[ $(openssl dgst -sha256 Pipfile.lock) == $(cat .heroku/python/Pipfile.lock.sha256) ]]; then
12-
# Don't skip installation if there are git deps.
13-
if ! grep -q 'git' Pipfile.lock; then
14-
echo "Skipping installation, as Pipfile.lock hasn't changed since last deploy." | indent
9+
# Previous versions of the buildpack used to cache the checksum of the lockfile to allow
10+
# for skipping pipenv install if the lockfile was unchanged. However, this is not always safe
11+
# to do (the lockfile can refer to dependencies that can change independently of the lockfile,
12+
# for example, when using a local non-editable file dependency), so we no longer ever skip
13+
# install, and instead defer to pipenv to determine whether install is actually a no-op.
14+
rm -f .heroku/python/Pipfile.lock.sha256
15+
16+
if [[ -f Pipfile ]]; then
17+
# Measure that we're using Pipenv.
18+
mcount "tool.pipenv"
19+
20+
# Skip installing dependencies using pip later.
21+
export SKIP_PIP_INSTALL=1
1522

16-
mcount "tool.pipenv"
17-
export SKIP_PIPENV_INSTALL=1
18-
export SKIP_PIP_INSTALL=1
19-
fi
20-
fi
23+
# Set Pip env vars
24+
# This reads certain environment variables set on the Heroku app config
25+
# and makes them accessible to the pip install process.
26+
#
27+
# PIP_EXTRA_INDEX_URL allows for an alternate pypi URL to be used.
28+
if [[ -r "$ENV_DIR/PIP_EXTRA_INDEX_URL" ]]; then
29+
PIP_EXTRA_INDEX_URL="$(cat "$ENV_DIR/PIP_EXTRA_INDEX_URL")"
30+
export PIP_EXTRA_INDEX_URL
31+
mcount "buildvar.PIP_EXTRA_INDEX_URL"
2132
fi
22-
fi
23-
24-
25-
if [ ! "$SKIP_PIPENV_INSTALL" ]; then
26-
# Pipenv support (Generate requirements.txt with pipenv).
27-
if [[ -f Pipfile ]]; then
28-
# Measure that we're using Pipenv.
29-
mcount "tool.pipenv"
3033

31-
# Skip pip install, later.
32-
export SKIP_PIP_INSTALL=1
34+
PIPENV_VERSION=$(get_requirement_version 'pipenv')
3335

34-
# Set Pip env vars
35-
# This reads certain environment variables set on the Heroku app config
36-
# and makes them accessible to the pip install process.
37-
#
38-
# PIP_EXTRA_INDEX_URL allows for an alternate pypi URL to be used.
39-
if [[ -r "$ENV_DIR/PIP_EXTRA_INDEX_URL" ]]; then
40-
PIP_EXTRA_INDEX_URL="$(cat "$ENV_DIR/PIP_EXTRA_INDEX_URL")"
41-
export PIP_EXTRA_INDEX_URL
42-
mcount "buildvar.PIP_EXTRA_INDEX_URL"
43-
fi
36+
/app/.heroku/python/bin/pip install --quiet --disable-pip-version-check --no-cache-dir "pipenv==${PIPENV_VERSION}"
4437

45-
PIPENV_VERSION=$(get_requirement_version 'pipenv')
38+
# Install the test dependencies, for CI.
39+
if [ "$INSTALL_TEST" ]; then
40+
puts-step "Installing test dependencies"
41+
/app/.heroku/python/bin/pipenv install --dev --system --deploy 2>&1 | cleanup | indent
4642

47-
/app/.heroku/python/bin/pip install --quiet --disable-pip-version-check --no-cache-dir "pipenv==${PIPENV_VERSION}"
43+
# Install the dependencies.
44+
elif [[ ! -f Pipfile.lock ]]; then
45+
puts-step "Installing dependencies with Pipenv ${PIPENV_VERSION}"
46+
/app/.heroku/python/bin/pipenv install --system --skip-lock 2>&1 | indent
4847

49-
# Install the test dependencies, for CI.
50-
if [ "$INSTALL_TEST" ]; then
51-
puts-step "Installing test dependencies"
52-
/app/.heroku/python/bin/pipenv install --dev --system --deploy 2>&1 | cleanup | indent
48+
else
49+
pipenv-to-pip Pipfile.lock > requirements.txt
50+
cp requirements.txt .heroku/python/requirements-declared.txt
5351

54-
# Install the dependencies.
55-
elif [[ ! -f Pipfile.lock ]]; then
56-
puts-step "Installing dependencies with Pipenv ${PIPENV_VERSION}"
57-
/app/.heroku/python/bin/pipenv install --system --skip-lock 2>&1 | indent
58-
59-
else
60-
pipenv-to-pip Pipfile.lock > requirements.txt
61-
cp requirements.txt .heroku/python/requirements-declared.txt
62-
openssl dgst -sha256 Pipfile.lock > .heroku/python/Pipfile.lock.sha256
63-
64-
puts-step "Installing dependencies with Pipenv ${PIPENV_VERSION}"
65-
/app/.heroku/python/bin/pipenv install --system --deploy 2>&1 | indent
66-
fi
52+
puts-step "Installing dependencies with Pipenv ${PIPENV_VERSION}"
53+
/app/.heroku/python/bin/pipenv install --system --deploy 2>&1 | indent
6754
fi
68-
else
69-
export SKIP_PIP_INSTALL=1
70-
pipenv-to-pip Pipfile.lock > requirements.txt
7155
fi

spec/fixtures/pipenv_editable/Pipfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[[source]]
2+
url = "https://pypi.org/simple"
3+
verify_ssl = true
4+
name = "pypi"
5+
6+
[packages]
7+
local-package-pyproject-toml = {file = "packages/local_package_pyproject_toml", editable = true}
8+
local-package-setup-py = {file = "packages/local_package_setup_py", editable = true}
9+
gunicorn = {git = "git+https://github.com/benoitc/gunicorn", ref = "20.1.0", editable = true}

spec/fixtures/pipenv_editable/Pipfile.lock

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env bash
2+
3+
# This file is run by the inline buildpack, and tests that editable requirements are
4+
# usable by buildpacks that run after the Python buildpack during the build.
5+
6+
set -euo pipefail
7+
8+
BUILD_DIR="${1}"
9+
10+
cd "${BUILD_DIR}"
11+
12+
exec bin/test-entrypoints
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
# This file is run by the inline buildpack.
4+
5+
set -euo pipefail
6+
7+
echo "Inline"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
exec bin/test-entrypoints
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
# List the filenames and contents of all .egg-link, .pth, and finder files in site-packages.
6+
find .heroku/python/lib/python*/site-packages/ -type f -and \( -name '*.egg-link' -or -name '*.pth' -or -name '__editable___*_finder.py' \) | sort | xargs -exec tail -n +1
7+
echo
8+
9+
echo -n "Running entrypoint for the pyproject.toml-based local package: "
10+
local_package_pyproject_toml
11+
12+
echo -n "Running entrypoint for the setup.py-based local package: "
13+
local_package_setup_py
14+
15+
echo -n "Running entrypoint for the VCS package: "
16+
gunicorn --version
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def hello():
2+
print("Hello pyproject.toml!")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[project]
2+
name = "local_package_pyproject_toml"
3+
version = "0.0.1"
4+
5+
[project.scripts]
6+
local_package_pyproject_toml = "local_package_pyproject_toml:hello"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def hello():
2+
print("Hello setup.py!")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[metadata]
2+
name = local_package_setup_py
3+
version = 0.0.1
4+
5+
[options]
6+
packages = local_package_setup_py
7+
8+
[options.entry_points]
9+
console_scripts =
10+
local_package_setup_py = local_package_setup_py:hello
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from setuptools import setup
2+
3+
setup()

spec/fixtures/requirements_editable/bin/test-entrypoints

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -euo pipefail
44

55
# List the filenames and contents of all .egg-link, .pth, and finder files in site-packages.
6-
find .heroku/python/lib*/*/site-packages/ -type f -and \( -name '*.egg-link' -or -name '*.pth' -or -name '__editable___*_finder.py' \) | sort | xargs -exec tail -n +1
6+
find .heroku/python/lib/python*/site-packages/ -type f -and \( -name '*.egg-link' -or -name '*.pth' -or -name '__editable___*_finder.py' \) | sort | xargs -exec tail -n +1
77
echo
88

99
echo -n "Running entrypoint for the pyproject.toml-based local package: "

0 commit comments

Comments
 (0)