diff --git a/CHANGELOG.md b/CHANGELOG.md index a0a02e9d0..b9d1d69f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) diff --git a/bin/steps/python b/bin/steps/python index 8388602ee..bf9af415a 100755 --- a/bin/steps/python +++ b/bin/steps/python @@ -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}" @@ -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}" @@ -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}" @@ -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}" diff --git a/spec/fixtures/pypy_3.6/requirements.txt b/spec/fixtures/pypy_3.6/requirements.txt deleted file mode 100644 index a42590beb..000000000 --- a/spec/fixtures/pypy_3.6/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -urllib3 diff --git a/spec/fixtures/pypy_3.6/runtime.txt b/spec/fixtures/pypy_3.6/runtime.txt deleted file mode 100644 index 52a99c835..000000000 --- a/spec/fixtures/pypy_3.6/runtime.txt +++ /dev/null @@ -1 +0,0 @@ -pypy3.6-7.3.2 diff --git a/spec/fixtures/python_version_invalid/runtime.txt b/spec/fixtures/python_version_invalid/runtime.txt index 400ad666f..504402610 100644 --- a/spec/fixtures/python_version_invalid/runtime.txt +++ b/spec/fixtures/python_version_invalid/runtime.txt @@ -1 +1 @@ -python-X.Y.Z +python-3.12.0invalid diff --git a/spec/hatchet/pipenv_spec.rb b/spec/hatchet/pipenv_spec.rb index 4bad709cf..a22daff8b 100644 --- a/spec/hatchet/pipenv_spec.rb +++ b/spec/hatchet/pipenv_spec.rb @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/spec/hatchet/python_update_warning_spec.rb b/spec/hatchet/python_update_warning_spec.rb index 38d012728..629d20a78 100644 --- a/spec/hatchet/python_update_warning_spec.rb +++ b/spec/hatchet/python_update_warning_spec.rb @@ -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 @@ -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 diff --git a/spec/hatchet/python_version_spec.rb b/spec/hatchet/python_version_spec.rb index 8dac5a993..56160a1c6 100644 --- a/spec/hatchet/python_version_spec.rb +++ b/spec/hatchet/python_version_spec.rb @@ -25,8 +25,12 @@ expect(clean_output(app.output)).to include(<<~OUTPUT) remote: -----> Python app detected remote: -----> Using Python version specified in runtime.txt + remote: ! remote: ! Requested runtime '#{requested_runtime}' 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 @@ -81,60 +85,51 @@ context 'when runtime.txt contains python-3.6.15' do let(:app) { Hatchet::Runner.new('spec/fixtures/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 include(<<~OUTPUT) - remote: -----> Python app detected - remote: -----> Using Python version specified in runtime.txt - 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 include(<<~OUTPUT) + remote: -----> Python app detected + remote: -----> Using Python version specified in runtime.txt + 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', 'python-3.6.15' - end end context 'when runtime.txt contains python-3.7.17' do let(:app) { Hatchet::Runner.new('spec/fixtures/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 include(<<~OUTPUT) - remote: -----> Python app detected - remote: -----> Using Python version specified in runtime.txt - 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 include(<<~OUTPUT) + remote: -----> Python app detected + remote: -----> Using Python version specified in runtime.txt + 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', 'python-3.7.17' - end end context 'when runtime.txt contains python-3.8.19' do @@ -171,7 +166,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', "python-#{LATEST_PYTHON_3_8}" end end @@ -200,37 +195,10 @@ include_examples 'builds with the requested Python version', LATEST_PYTHON_3_12 end - context 'when runtime.txt contains pypy3.6-7.3.2' do - let(:app) { Hatchet::Runner.new('spec/fixtures/pypy_3.6', allow_failure: true) } - - context 'when using Heroku-20', stacks: %w[heroku-20] do - it 'aborts the build with a sunset message' 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: ! PyPy is no longer supported by the latest version of this buildpack. - remote: ! - remote: ! Please switch to one of the supported CPython versions by updating your - remote: ! runtime.txt file. See: - remote: ! https://devcenter.heroku.com/articles/python-support - remote: ! - OUTPUT - end - end - end - - context 'when using Heroku-22', stacks: %w[heroku-22] do - # The beta PyPy support is deprecated and so not being made available for new stacks. - include_examples 'aborts the build with a runtime not available message', 'pypy3.6-7.3.2' - end - end - context 'when runtime.txt contains an invalid python version string' do let(:app) { Hatchet::Runner.new('spec/fixtures/python_version_invalid', allow_failure: true) } - include_examples 'aborts the build with a runtime not available message', 'python-X.Y.Z' + include_examples 'aborts the build with a runtime not available message', 'python-3.12.0invalid' end context 'when runtime.txt contains stray whitespace' do