Skip to content

Deprecate support for the runtime.txt file #1743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased]

- Deprecated support for the `runtime.txt` file. ([#1743](https://github.com/heroku/heroku-buildpack-python/pull/1743))
- Improved the error messages shown when `.python-version`, `runtime.txt` or `Pipfile.lock` contain an invalid Python version. ([#1743](https://github.com/heroku/heroku-buildpack-python/pull/1743))

## [v275] - 2025-01-13

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ For example, to request the latest patch release of Python 3.13, create a `.pyth
the root directory of your app containing:
`3.13`

We strongly recommend that you use the major version form instead of pinning to an exact version,
since it will allow your app to receive Python security updates.

The buildpack will look for a Python version in the following places (in descending order of precedence):

1. `runtime.txt` file (deprecated)
Expand Down
26 changes: 25 additions & 1 deletion bin/compile
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ meta_set "python_version_reason" "${python_version_origin}"

# TODO: More strongly recommend specifying a Python version (eg switch the messaging to
# be a warning instead, after version resolution, and mention .python-version inline)
# TODO: Add runtime.txt deprecation warning.
case "${python_version_origin}" in
default)
output::step "No Python version was specified. Using the buildpack default: Python ${requested_python_version}"
Expand All @@ -145,6 +144,31 @@ python_major_version="${python_full_version%.*}"
meta_set "python_version" "${python_full_version}"
meta_set "python_version_major" "${python_major_version}"

if [[ "${python_version_origin}" == "runtime.txt" ]]; then
output::warning <<-EOF
Warning: The runtime.txt file is deprecated.

The runtime.txt file is deprecated since it has been replaced
by the more widely supported .python-version file.

Please delete your runtime.txt file and create a new file named:
.python-version

Make sure to include the '.' at the start of the filename.

In the new file, specify your app's Python version without
quotes or a 'python-' prefix. For example:
${python_major_version}

We strongly recommend that you use the major version form
instead of pinning to an exact version, since it will allow
your app to receive Python security updates.

In the future support for runtime.txt will be removed and
this warning will be made an error.
EOF
fi

cache::restore "${BUILD_DIR}" "${CACHE_DIR}" "${STACK}" "${cached_python_full_version}" "${python_full_version}" "${package_manager}"

# The directory for the .profile.d scripts.
Expand Down
101 changes: 62 additions & 39 deletions lib/python_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ PYTHON_FULL_VERSION_REGEX="${INT_REGEX}\.${INT_REGEX}\.${INT_REGEX}"
# resolved to an exact Python version.
#
# If an app specifies the Python version via multiple means, then the order of precedence is:
# 1. runtime.txt
# 2. .python-version
# 3. Pipfile.lock (`python_full_version` field)
# 4. Pipfile.lock (`python_version` field)
# 1. `runtime.txt` file (deprecated)
# 2. `.python-version` file (recommended)
# 3. The `python_full_version` field in the `Pipfile.lock` file
# 4. The `python_version` field in the `Pipfile.lock` file
#
# If a version wasn't specified by the app, then new apps/those with an empty cache will use
# a buildpack default version for the first build, and then subsequent cached builds will use
Expand Down Expand Up @@ -100,21 +100,29 @@ function python_version::parse_runtime_txt() {
output::error <<-EOF
Error: Invalid Python version in runtime.txt.

The Python version specified in 'runtime.txt' isn't in
the correct format.
The Python version specified in your runtime.txt file isn't
in the correct format.

The following file contents were found:
The following file contents were found, which aren't valid:
${contents}

However, the version must be specified as either:
1. 'python-<major>.<minor>' (recommended, for automatic patch updates)
2. 'python-<major>.<minor>.<patch>' (to pin to an exact patch version)
However, the runtime.txt file is deprecated since it has
been replaced by the .python-version file. As such, we
recommend that you switch to using a .python-version file
instead of fixing your runtime.txt file.

Remember to include the 'python-' prefix. Comments aren't supported.
Please delete your runtime.txt file and create a new file named:
.python-version

For example, to request the latest version of Python ${DEFAULT_PYTHON_MAJOR_VERSION},
update the 'runtime.txt' file so it contains:
python-${DEFAULT_PYTHON_MAJOR_VERSION}
Make sure to include the '.' at the start of the filename.

In the new file, specify your app's Python version without
quotes or a 'python-' prefix. For example:
${DEFAULT_PYTHON_MAJOR_VERSION}

We strongly recommend that you use the major version form
instead of pinning to an exact version, since it will allow
your app to receive Python security updates.
EOF
meta_set "failure_reason" "runtime-txt::invalid-version"
exit 1
Expand Down Expand Up @@ -144,22 +152,26 @@ function python_version::parse_python_version_file() {
output::error <<-EOF
Error: Invalid Python version in .python-version.

The Python version specified in '.python-version' isn't in
the correct format.
The Python version specified in your .python-version file
isn't in the correct format.

The following version was found:
${line}

However, the version must be specified as either:
1. '<major>.<minor>' (recommended, for automatic patch updates)
2. '<major>.<minor>.<patch>' (to pin to an exact patch version)
However, the Python version must be specified as either:
1. The major version only: 3.X (recommended)
2. An exact patch version: 3.X.Y

Don't include quotes or a 'python-' prefix. To include
comments, add them on their own line, prefixed with '#'.

For example, to request the latest version of Python ${DEFAULT_PYTHON_MAJOR_VERSION},
update the '.python-version' file so it contains:
update your .python-version file so it contains:
${DEFAULT_PYTHON_MAJOR_VERSION}

We strongly recommend that you use the major version form
instead of pinning to an exact version, since it will allow
your app to receive Python security updates.
EOF
meta_set "failure_reason" "python-version-file::invalid-version"
exit 1
Expand All @@ -169,10 +181,13 @@ function python_version::parse_python_version_file() {
output::error <<-EOF
Error: Invalid Python version in .python-version.

No Python version was found in the '.python-version' file.
No Python version was found in your .python-version file.

Update the file so that it contains a valid Python version
such as '${DEFAULT_PYTHON_MAJOR_VERSION}'.
Update the file so that it contains a valid Python version.

For example, to request the latest version of Python ${DEFAULT_PYTHON_MAJOR_VERSION},
update your .python-version file so it contains:
${DEFAULT_PYTHON_MAJOR_VERSION}

If the file already contains a version, check the line doesn't
begin with a '#', otherwise it will be treated as a comment.
Expand All @@ -184,8 +199,7 @@ function python_version::parse_python_version_file() {
output::error <<-EOF
Error: Invalid Python version in .python-version.

Multiple Python versions were found in the '.python-version'
file:
Multiple versions were found in your .python-version file:

$(
IFS=$'\n'
Expand All @@ -194,8 +208,8 @@ function python_version::parse_python_version_file() {

Update the file so it contains only one Python version.

If the additional versions are actually comments, prefix
those lines with '#'.
If you have added comments to the file, make sure that those
lines begin with a '#', so that they are ignored.
EOF
meta_set "failure_reason" "python-version-file::multiple-versions"
exit 1
Expand Down Expand Up @@ -245,20 +259,24 @@ function python_version::read_pipenv_python_version() {
echo "${version}"
else
output::error <<-EOF
Error: Invalid Python version in Pipfile / Pipfile.lock.
Error: Invalid Python version in Pipfile.lock.

The Python version specified in Pipfile / Pipfile.lock by the
'python_version' or 'python_full_version' field isn't valid.
The Python version specified in your Pipfile.lock file by the
'python_version' or 'python_full_version' fields isn't valid.

The following version was found:
${version}

However, the version must be specified as either:
1. '<major>.<minor>' (recommended, for automatic patch updates)
2. '<major>.<minor>.<patch>' (to pin to an exact patch version)
However, the Python version must be specified as either:
1. The major version only: 3.X (recommended)
2. An exact patch version: 3.X.Y

Please update your Pipfile to use a valid Python version and
then run 'pipenv lock' to regenerate Pipfile.lock.

Please update your 'Pipfile' to use a valid Python version and
then run 'pipenv lock' to regenerate the lockfile.
We strongly recommend that you use the major version form
instead of pinning to an exact version, since it will allow
your app to receive Python security updates.

For more information, see:
https://pipenv.pypa.io/en/stable/specifiers.html#specifying-versions-of-python
Expand Down Expand Up @@ -297,10 +315,15 @@ function python_version::resolve_python_version() {
As such, it's no longer supported by this buildpack:
https://devcenter.heroku.com/articles/python-support#supported-python-versions

Please upgrade to at least Python 3.${OLDEST_SUPPORTED_PYTHON_3_MINOR_VERSION} by creating a
.python-version file in the root directory of your app,
that contains a Python version like:
3.${OLDEST_SUPPORTED_PYTHON_3_MINOR_VERSION}
Please upgrade to at least Python 3.${OLDEST_SUPPORTED_PYTHON_3_MINOR_VERSION} by configuring an
explicit Python version for your app.

Create a .python-version file in the root directory of your
app, that contains a Python version like:
3.${NEWEST_SUPPORTED_PYTHON_3_MINOR_VERSION}

When creating this file make sure to include the '.' at the
start of the filename.
EOF
else
output::error <<-EOF
Expand Down
40 changes: 24 additions & 16 deletions spec/hatchet/pipenv_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,24 @@
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote:
remote: ! Error: Invalid Python version in Pipfile / Pipfile.lock.
remote: ! Error: Invalid Python version in Pipfile.lock.
remote: !
remote: ! The Python version specified in Pipfile / Pipfile.lock by the
remote: ! 'python_version' or 'python_full_version' field isn't valid.
remote: ! The Python version specified in your Pipfile.lock file by the
remote: ! 'python_version' or 'python_full_version' fields isn't valid.
remote: !
remote: ! The following version was found:
remote: ! ^3.12
remote: !
remote: ! However, the version must be specified as either:
remote: ! 1. '<major>.<minor>' (recommended, for automatic patch updates)
remote: ! 2. '<major>.<minor>.<patch>' (to pin to an exact patch version)
remote: ! However, the Python version must be specified as either:
remote: ! 1. The major version only: 3.X (recommended)
remote: ! 2. An exact patch version: 3.X.Y
remote: !
remote: ! Please update your 'Pipfile' to use a valid Python version and
remote: ! then run 'pipenv lock' to regenerate the lockfile.
remote: ! Please update your Pipfile to use a valid Python version and
remote: ! then run 'pipenv lock' to regenerate Pipfile.lock.
remote: !
remote: ! We strongly recommend that you use the major version form
remote: ! instead of pinning to an exact version, since it will allow
remote: ! your app to receive Python security updates.
remote: !
remote: ! For more information, see:
remote: ! https://pipenv.pypa.io/en/stable/specifiers.html#specifying-versions-of-python
Expand All @@ -251,20 +255,24 @@
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote:
remote: ! Error: Invalid Python version in Pipfile / Pipfile.lock.
remote: ! Error: Invalid Python version in Pipfile.lock.
remote: !
remote: ! The Python version specified in Pipfile / Pipfile.lock by the
remote: ! 'python_version' or 'python_full_version' field isn't valid.
remote: ! The Python version specified in your Pipfile.lock file by the
remote: ! 'python_version' or 'python_full_version' fields isn't valid.
remote: !
remote: ! The following version was found:
remote: ! 3.9.*
remote: !
remote: ! However, the version must be specified as either:
remote: ! 1. '<major>.<minor>' (recommended, for automatic patch updates)
remote: ! 2. '<major>.<minor>.<patch>' (to pin to an exact patch version)
remote: ! However, the Python version must be specified as either:
remote: ! 1. The major version only: 3.X (recommended)
remote: ! 2. An exact patch version: 3.X.Y
remote: !
remote: ! Please update your Pipfile to use a valid Python version and
remote: ! then run 'pipenv lock' to regenerate Pipfile.lock.
remote: !
remote: ! Please update your 'Pipfile' to use a valid Python version and
remote: ! then run 'pipenv lock' to regenerate the lockfile.
remote: ! We strongly recommend that you use the major version form
remote: ! instead of pinning to an exact version, since it will allow
remote: ! your app to receive Python security updates.
remote: !
remote: ! For more information, see:
remote: ! https://pipenv.pypa.io/en/stable/specifiers.html#specifying-versions-of-python
Expand Down
22 changes: 22 additions & 0 deletions spec/hatchet/python_update_warning_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote: -----> Using Python 3.9.0 specified in runtime.txt
remote:
remote: ! Warning: The runtime.txt file is deprecated.
remote: !
remote: ! The runtime.txt file is deprecated since it has been replaced
remote: ! by the more widely supported .python-version file.
remote: !
remote: ! Please delete your runtime.txt file and create a new file named:
remote: ! .python-version
remote: !
remote: ! Make sure to include the '.' at the start of the filename.
remote: !
remote: ! In the new file, specify your app's Python version without
remote: ! quotes or a 'python-' prefix. For example:
remote: ! 3.9
remote: !
remote: ! We strongly recommend that you use the major version form
remote: ! instead of pinning to an exact version, since it will allow
remote: ! your app to receive Python security updates.
remote: !
remote: ! In the future support for runtime.txt will be removed and
remote: ! this warning will be made an error.
remote:
remote: -----> Installing Python 3.9.0
remote:
remote: ! Warning: Support for Python 3.9 is ending soon!
Expand Down
Loading