Skip to content

Commit

Permalink
Improve the EOL Python error messaging
Browse files Browse the repository at this point in the history
For cases where a requested Python version is both (a) EOL, and (b) was
never built for that stack (such as is the case when we add new stacks),
previously the generic "version isn't available for this stack" error
message was shown instead of the more specific EOL Python version error
message.

Now, the EOL version check is performed first before the S3 presence
check, so the more specific EOL message is shown for this case.

In addition to improving the UX, making this change now reduces the
test fixture churn both when we add a new stack and for #1567.

I've also dropped the "PyPy is no longer supported" error message
and associated test, since very few apps ever used it and it's now been
19 months since support was removed in #1364, so it's fine to show the
generic "Python version isn't available" error message for it instead.

GUS-W-15541279.
  • Loading branch information
edmorley committed Apr 18, 2024
1 parent a9bcb10 commit b04f3eb
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 180 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [Unreleased]

- Improved the error message shown for EOL Python versions when using a stack for which those versions were never built. ([#1570](https://github.com/heroku/heroku-buildpack-python/pull/1570))
- Fixed the "Python security update is available" warning being shown when the requested version is newer than the latest version known to the buildpack. ([#1569](https://github.com/heroku/heroku-buildpack-python/pull/1569))
- Fixed glibc warnings seen when downgrading the stack version. ([#1568](https://github.com/heroku/heroku-buildpack-python/pull/1568))
- Adjusted compiler options used to build Python for improved parity with the Docker Hub Python images. ([#1566](https://github.com/heroku/heroku-buildpack-python/pull/1566))
Expand Down
58 changes: 30 additions & 28 deletions bin/steps/python
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ PYTHON_VERSION=$(cat runtime.txt)
PYTHON_VERSION="${PYTHON_VERSION##+([[:space:]])}"
PYTHON_VERSION="${PYTHON_VERSION%%+([[:space:]])}"

# The location of the pre-compiled python binary.
PYTHON_URL="${S3_BASE_URL}/${STACK}/runtimes/${PYTHON_VERSION}.tar.gz"

if ! curl --output /dev/null --silent --head --fail --retry 3 --retry-connrefused --connect-timeout 10 "${PYTHON_URL}"; then
puts-warn "Requested runtime '${PYTHON_VERSION}' is not available for this stack (${STACK})."
puts-warn "For supported versions, see: https://devcenter.heroku.com/articles/python-support"
exit 1
fi

function eol_python_version_error() {
local major_version="${1}"
local eol_date="${2}"
Expand All @@ -23,14 +14,39 @@ function eol_python_version_error() {
puts-warn "therefore no longer receiving security updates:"
puts-warn "https://devguide.python.org/versions/#supported-versions"
puts-warn
puts-warn "As such, it is no longer supported by the latest version of this buildpack."
puts-warn "As such, it is no longer supported by this buildpack."
puts-warn
puts-warn "Please upgrade to a newer Python version. See:"
puts-warn "https://devcenter.heroku.com/articles/python-runtimes"
puts-warn "Please upgrade to a newer Python version."
puts-warn
puts-warn "For a list of the supported Python versions, see:"
puts-warn "https://devcenter.heroku.com/articles/python-support#supported-runtimes"
puts-warn
exit 1
}

# We check for EOL prior to checking if the archive exists on S3, to ensure the more specific EOL error
# message is still shown for newer stacks where the EOL Python versions might not have been built.
case "${PYTHON_VERSION}" in
python-3.7.+([0-9]))
eol_python_version_error "3.7" "June 27th, 2023"
;;
python-3.6.+([0-9]))
eol_python_version_error "3.6" "December 23rd, 2021"
;;
esac

PYTHON_URL="${S3_BASE_URL}/${STACK}/runtimes/${PYTHON_VERSION}.tar.gz"

if ! curl --output /dev/null --silent --head --fail --retry 3 --retry-connrefused --connect-timeout 10 "${PYTHON_URL}"; then
puts-warn
puts-warn "Requested runtime '${PYTHON_VERSION}' is not available for this stack (${STACK})."
puts-warn
puts-warn "For a list of the supported Python versions, see:"
puts-warn "https://devcenter.heroku.com/articles/python-support#supported-runtimes"
puts-warn
exit 1
fi

function warn_if_patch_update_available() {
local requested_version="${1}"
local latest_patch_version="${2}"
Expand All @@ -45,6 +61,8 @@ function warn_if_patch_update_available() {
fi
}

# We wait until now to display outdated Python version warnings, since we only want to show them
# if there weren't any errors with the version to avoid adding noise to the error messages.
case "${PYTHON_VERSION}" in
python-3.12.*)
warn_if_patch_update_available "${PYTHON_VERSION}" "${LATEST_312}"
Expand All @@ -71,22 +89,6 @@ case "${PYTHON_VERSION}" in
puts-warn
warn_if_patch_update_available "${PYTHON_VERSION}" "${LATEST_38}"
;;
python-3.7.*)
eol_python_version_error "3.7" "June 27th, 2023"
;;
python-3.6.*)
eol_python_version_error "3.6" "December 23rd, 2021"
;;
pypy*)
puts-warn
puts-warn "PyPy is no longer supported by the latest version of this buildpack."
puts-warn
puts-warn "Please switch to one of the supported CPython versions by updating your"
puts-warn "runtime.txt file. See:"
puts-warn "https://devcenter.heroku.com/articles/python-support"
puts-warn
exit 1
;;
esac

mcount "version.python.${PYTHON_VERSION}"
Expand Down
1 change: 0 additions & 1 deletion spec/fixtures/pypy_3.6/requirements.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/fixtures/pypy_3.6/runtime.txt

This file was deleted.

2 changes: 1 addition & 1 deletion spec/fixtures/python_version_invalid/runtime.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
python-X.Y.Z
python-3.12.0invalid
101 changes: 52 additions & 49 deletions spec/hatchet/pipenv_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Requested runtime 'python-#{requested_version}' is not available for this stack (#{app.stack}).
remote: ! For supported versions, see: https://devcenter.heroku.com/articles/python-support
remote: !
remote: ! For a list of the supported Python versions, see:
remote: ! https://devcenter.heroku.com/articles/python-support#supported-runtimes
remote: !
OUTPUT
end
end
Expand Down Expand Up @@ -78,60 +82,51 @@
context 'with a Pipfile.lock containing python_version 3.6' do
let(:app) { Hatchet::Runner.new('spec/fixtures/pipenv_python_3.6', allow_failure: true) }

context 'when using Heroku-20', stacks: %w[heroku-20] do
it 'aborts the build with an EOL message' do
app.deploy do |app|
expect(clean_output(app.output)).to match(Regexp.new(<<~OUTPUT))
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Python 3.6 reached upstream end-of-life on December 23rd, 2021, and is
remote: ! therefore no longer receiving security updates:
remote: ! https://devguide.python.org/versions/#supported-versions
remote: !
remote: ! As such, it is no longer supported by the latest version of this buildpack.
remote: !
remote: ! Please upgrade to a newer Python version. See:
remote: ! https://devcenter.heroku.com/articles/python-runtimes
remote: !
OUTPUT
end
it 'aborts the build with an EOL message' do
app.deploy do |app|
expect(clean_output(app.output)).to match(Regexp.new(<<~OUTPUT))
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Python 3.6 reached upstream end-of-life on December 23rd, 2021, and is
remote: ! therefore no longer receiving security updates:
remote: ! https://devguide.python.org/versions/#supported-versions
remote: !
remote: ! As such, it is no longer supported by this buildpack.
remote: !
remote: ! Please upgrade to a newer Python version.
remote: !
remote: ! For a list of the supported Python versions, see:
remote: ! https://devcenter.heroku.com/articles/python-support#supported-runtimes
remote: !
OUTPUT
end
end

context 'when using Heroku-22', stacks: %w[heroku-22] do
# Python 3.6 is EOL, so has not been built for newer stacks.
include_examples 'aborts the build with a runtime not available message (Pipenv)', '3.6.15'
end
end

context 'with a Pipfile.lock containing python_version 3.7' do
let(:app) { Hatchet::Runner.new('spec/fixtures/pipenv_python_3.7', allow_failure: true) }

context 'when using Heroku-20', stacks: %w[heroku-20] do
it 'aborts the build with an EOL message' do
app.deploy do |app|
expect(clean_output(app.output)).to match(Regexp.new(<<~OUTPUT))
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Python 3.7 reached upstream end-of-life on June 27th, 2023, and is
remote: ! therefore no longer receiving security updates:
remote: ! https://devguide.python.org/versions/#supported-versions
remote: !
remote: ! As such, it is no longer supported by the latest version of this buildpack.
remote: !
remote: ! Please upgrade to a newer Python version. See:
remote: ! https://devcenter.heroku.com/articles/python-runtimes
remote: !
OUTPUT
end
it 'aborts the build with an EOL message' do
app.deploy do |app|
expect(clean_output(app.output)).to match(Regexp.new(<<~OUTPUT))
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Python 3.7 reached upstream end-of-life on June 27th, 2023, and is
remote: ! therefore no longer receiving security updates:
remote: ! https://devguide.python.org/versions/#supported-versions
remote: !
remote: ! As such, it is no longer supported by this buildpack.
remote: !
remote: ! Please upgrade to a newer Python version.
remote: !
remote: ! For a list of the supported Python versions, see:
remote: ! https://devcenter.heroku.com/articles/python-support#supported-runtimes
remote: !
OUTPUT
end
end

context 'when using Heroku-22', stacks: %w[heroku-22] do
include_examples 'aborts the build with a runtime not available message (Pipenv)', '3.7.17'
end
end

context 'with a Pipfile.lock containing python_version 3.8' do
Expand Down Expand Up @@ -167,7 +162,7 @@
context 'when using Heroku-22', stacks: %w[heroku-22] do
let(:allow_failure) { true }

# Python 3.8 is in the security fix only stage of its lifecycle, so has not been built for newer stacks.
# We only support Python 3.8 on Heroku-20 and older.
include_examples 'aborts the build with a runtime not available message (Pipenv)', LATEST_PYTHON_3_8
end
end
Expand Down Expand Up @@ -228,8 +223,12 @@
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Requested runtime '^3.9' is not available for this stack (#{app.stack}).
remote: ! For supported versions, see: https://devcenter.heroku.com/articles/python-support
remote: !
remote: ! For a list of the supported Python versions, see:
remote: ! https://devcenter.heroku.com/articles/python-support#supported-runtimes
remote: !
OUTPUT
end
end
Expand All @@ -243,8 +242,12 @@
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote: -----> Using Python version specified in Pipfile.lock
remote: !
remote: ! Requested runtime 'python-X.Y.Z' is not available for this stack (#{app.stack}).
remote: ! For supported versions, see: https://devcenter.heroku.com/articles/python-support
remote: !
remote: ! For a list of the supported Python versions, see:
remote: ! https://devcenter.heroku.com/articles/python-support#supported-runtimes
remote: !
OUTPUT
end
end
Expand Down
31 changes: 6 additions & 25 deletions spec/hatchet/python_update_warning_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,18 @@
end
end

RSpec.shared_examples 'warns about both EOL major version and the patch update' do |requested_version, latest_version|
it 'warns there is a Python update available' do
app.deploy do |app|
expect(clean_output(app.output)).to match(Regexp.new(<<~REGEX))
remote: -----> Python app detected
remote: -----> Using Python version specified in runtime.txt
remote: !
remote: ! Python .* reached upstream end-of-life on .*, and is
remote: ! therefore no longer receiving security updates:
remote: ! https://devguide.python.org/versions/#supported-versions
remote: !
remote: ! Upgrade to a newer Python version as soon as possible to keep your app secure.
remote: ! See: https://devcenter.heroku.com/articles/python-runtimes
remote: !
remote: !
remote: ! A Python security update is available! Upgrade as soon as possible to: python-#{latest_version}
remote: ! See: https://devcenter.heroku.com/articles/python-runtimes
remote: !
remote: -----> Installing python-#{requested_version}
REGEX
end
end
end

RSpec.shared_examples 'aborts the build without showing an update warning' do |requested_version|
it 'aborts the build without showing an update warning' do
app.deploy do |app|
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote: -----> Using Python version specified in runtime.txt
remote: !
remote: ! Requested runtime 'python-#{requested_version}' is not available for this stack (#{app.stack}).
remote: ! For supported versions, see: https://devcenter.heroku.com/articles/python-support
remote: !
remote: ! For a list of the supported Python versions, see:
remote: ! https://devcenter.heroku.com/articles/python-support#supported-runtimes
remote: !
OUTPUT
end
end
Expand Down Expand Up @@ -89,6 +69,7 @@
context 'when using Heroku-22', stacks: %w[heroku-22] do
let(:allow_failure) { true }

# We only support Python 3.8 on Heroku-20 and older.
include_examples 'aborts the build without showing an update warning', '3.8.12'
end
end
Expand Down
Loading

0 comments on commit b04f3eb

Please sign in to comment.