Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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 messages shown when an app is missing the necessary Python package manager files. ([#1608](https://github.com/heroku/heroku-buildpack-python/pull/1608))

## [v252] - 2024-06-17

Expand Down
35 changes: 35 additions & 0 deletions bin/compile
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,41 @@ else
CACHED_PYTHON_STACK=$STACK
fi

# TODO: Move this into a new package manager handling implementation when adding Poetry support.
if [[ ! -f requirements.txt && ! -f Pipfile && ! -f setup.py ]]; then
puts-warn
puts-warn "Error: No supported Python package manager files were found."
puts-warn
# We intentionally don't mention `setup.py` here since it's being removed soon.
puts-warn "A Python app on Heroku must have either a 'requirements.txt' or"
puts-warn "'Pipfile' file in the root directory of its source code, so the"
puts-warn "buildpack knows which dependencies to install."
puts-warn
puts-warn "Currently the root directory of your app contains:"
puts-warn
# TODO: Overhaul logging helpers so they can handle prefixing multi-line strings, and switch to them.
# shellcheck disable=SC2012 # Using `ls` instead of `find` is absolutely fine for this use case.
ls -1 --indicator-style=slash "${BUILD_DIR}" | sed 's/^/ ! /'
puts-warn
puts-warn "If you believe your app already has a 'requirements.txt' or"
puts-warn "'Pipfile' file, check that:"
puts-warn
puts-warn "1. The file is in the top level directory (not a subdirectory)."
puts-warn "2. The filename has the correct spelling and capitalisation."
puts-warn "3. The filename isn't listed in '.gitignore' or '.slugignore'."
puts-warn
puts-warn "Otherwise, please create a 'requirements.txt' file in the root"
puts-warn "of your app source, which lists your app's Python dependencies"
puts-warn "(the file can be empty if your app has no dependencies)."
puts-warn
puts-warn "For help with using Python on Heroku, see:"
puts-warn "https://devcenter.heroku.com/articles/getting-started-with-python"
puts-warn "https://devcenter.heroku.com/articles/python-support"
puts-warn
meta_set "failure_reason" "package-manager-not-found"
exit 1
fi

# Pipenv Python version support.
# Detect the version of Python requested from a Pipfile (e.g. python_version or python_full_version).
# Convert it to a runtime.txt file.
Expand Down
75 changes: 70 additions & 5 deletions bin/detect
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,74 @@ set -euo pipefail

BUILD_DIR="${1}"

# Exit early if app is clearly not Python.
if [[ ! -f "$BUILD_DIR/requirements.txt" ]] && [[ ! -f "$BUILD_DIR/setup.py" ]] && [[ ! -f "$BUILD_DIR/Pipfile" ]]; then
exit 1
fi
# Filenames that if found in a project mean it should be treated as a Python project,
# and so pass this buildpack's detection phase.
#
# This list is deliberately larger than just the list of supported package manager files,
# so that Python projects that are missing some of the required files still pass detection,
# allowing us to show a helpful error message during the build phase.
KNOWN_PYTHON_PROJECT_FILES=(
.python-version
app.py
main.py
manage.py
pdm.lock
Pipfile
Pipfile.lock
poetry.lock
pyproject.toml
requirements.txt
runtime.txt
setup.cfg
setup.py
uv.lock
)

echo Python
for filename in "${KNOWN_PYTHON_PROJECT_FILES[@]}"; do
if [[ -f "${BUILD_DIR}/${filename}" ]]; then
echo "Python"
exit 0
fi
done

# Cytokine incorrectly indents the first line, so we have to leave it empty.
echo 1>&2

# Note: This error message intentionally doesn't list all of the filetypes above,
# since during compile the build will still require a package manager file, so it
# makes sense to describe the stricter requirements up front.
# TODO: Overhaul logging helpers so they can handle prefixing multi-line strings, and switch to them.
sed 's/^/ ! /' 1>&2 << EOF
Error: Unable to find any Python project files.

The Python buildpack is set on this app, however, no recognised
Python related files were found.

A Python app on Heroku must have either a 'requirements.txt' or
'Pipfile' file in the root directory of its source code, so the
buildpack knows which dependencies to install.

Currently the root directory of your app contains:

$(ls -1 --indicator-style=slash "${BUILD_DIR}")

If you believe your app already has a 'requirements.txt' or
'Pipfile' file, check that:

1. The file is in the top level directory (not a subdirectory).
2. The filename has the correct spelling and capitalisation.
3. The filename isn't listed in '.gitignore' or '.slugignore'.

Otherwise, please create a 'requirements.txt' file in the root
of your app source, which lists your app's Python dependencies
(the file can be empty if your app has no dependencies).

For help with using Python on Heroku, see:
https://devcenter.heroku.com/articles/getting-started-with-python
https://devcenter.heroku.com/articles/python-support

If you are trying to deploy an app written in another language,
you need to change the list of buildpacks set on your app.
EOF

exit 1
Empty file.
Empty file.
33 changes: 33 additions & 0 deletions spec/hatchet/detect_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,39 @@
app.deploy do |app|
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> App not compatible with buildpack: #{DEFAULT_BUILDPACK_URL}
remote:
remote: ! Error: Unable to find any Python project files.
remote: !
remote: ! The Python buildpack is set on this app, however, no recognised
remote: ! Python related files were found.
remote: !
remote: ! A Python app on Heroku must have either a 'requirements.txt' or
remote: ! 'Pipfile' file in the root directory of its source code, so the
remote: ! buildpack knows which dependencies to install.
remote: !
remote: ! Currently the root directory of your app contains:
remote: !
remote: ! README.md
remote: ! subdir/
remote: !
remote: ! If you believe your app already has a 'requirements.txt' or
remote: ! 'Pipfile' file, check that:
remote: !
remote: ! 1. The file is in the top level directory (not a subdirectory).
remote: ! 2. The filename has the correct spelling and capitalisation.
remote: ! 3. The filename isn't listed in '.gitignore' or '.slugignore'.
remote: !
remote: ! Otherwise, please create a 'requirements.txt' file in the root
remote: ! of your app source, which lists your app's Python dependencies
remote: ! (the file can be empty if your app has no dependencies).
remote: !
remote: ! For help with using Python on Heroku, see:
remote: ! https://devcenter.heroku.com/articles/getting-started-with-python
remote: ! https://devcenter.heroku.com/articles/python-support
remote: !
remote: ! If you are trying to deploy an app written in another language,
remote: ! you need to change the list of buildpacks set on your app.
remote:
remote: More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
OUTPUT
end
Expand Down
44 changes: 44 additions & 0 deletions spec/hatchet/package_manager_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require_relative '../spec_helper'

RSpec.describe 'Package manager support' do
context 'when there are no supported package manager files' do
let(:app) { Hatchet::Runner.new('spec/fixtures/runtime_txt_only', allow_failure: true) }

it 'fails the build with an informative error message' do
app.deploy do |app|
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> Python app detected
remote: !
remote: ! Error: No supported Python package manager files were found.
remote: !
remote: ! A Python app on Heroku must have either a 'requirements.txt' or
remote: ! 'Pipfile' file in the root directory of its source code, so the
remote: ! buildpack knows which dependencies to install.
remote: !
remote: ! Currently the root directory of your app contains:
remote: !
remote: ! runtime.txt
remote: ! subdir/
remote: !
remote: ! If you believe your app already has a 'requirements.txt' or
remote: ! 'Pipfile' file, check that:
remote: !
remote: ! 1. The file is in the top level directory (not a subdirectory).
remote: ! 2. The filename has the correct spelling and capitalisation.
remote: ! 3. The filename isn't listed in '.gitignore' or '.slugignore'.
remote: !
remote: ! Otherwise, please create a 'requirements.txt' file in the root
remote: ! of your app source, which lists your app's Python dependencies
remote: ! (the file can be empty if your app has no dependencies).
remote: !
remote: ! For help with using Python on Heroku, see:
remote: ! https://devcenter.heroku.com/articles/getting-started-with-python
remote: ! https://devcenter.heroku.com/articles/python-support
remote: !
OUTPUT
end
end
end
end
6 changes: 0 additions & 6 deletions spec/hatchet/python_version_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,6 @@
include_examples 'builds with the requested Python version', LATEST_PYTHON_3_12
end

context 'when there is only a runtime.txt and no requirements.txt', skip: 'not currently supported (W-8720280)' do
let(:app) { Hatchet::Runner.new('spec/fixtures/runtime_txt_only', allow_failure: true) }

include_examples 'builds with the requested Python version', LATEST_PYTHON_3_12
end

context 'when the requested Python version has changed since the last build' do
let(:app) { Hatchet::Runner.new('spec/fixtures/python_3.11') }

Expand Down