diff --git a/CHANGELOG.md b/CHANGELOG.md index 8223b418..4379617d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- Updated buildpack-generated warning messages to use colour and be more consistently formatted. ([#1666](https://github.com/heroku/heroku-buildpack-python/pull/1666)) ## [v261] - 2024-10-14 diff --git a/bin/compile b/bin/compile index f751965f..f318f0fa 100755 --- a/bin/compile +++ b/bin/compile @@ -142,13 +142,6 @@ fi package_manager="$(package_manager::determine_package_manager "${BUILD_DIR}")" meta_set "package_manager" "${package_manager}" -# TODO: Move this warning to lib/package_manager.sh once `output::warning()` exists -# (puts-warn outputs to stdout, which would break `determine_package_manager()` as is). -# TODO: Adjust this warning to mention support for missing Pipfile.lock will be removed soon. -if [[ "${package_manager}" == "pipenv" && ! -f "${BUILD_DIR}/Pipfile.lock" ]]; then - puts-warn "No 'Pipfile.lock' found! We recommend you commit this into your repository." -fi - # We use the Bash 4.3+ `nameref` feature to pass back multiple values from this function # without having to hardcode globals. See: https://stackoverflow.com/a/38997681 python_version::read_requested_python_version "${BUILD_DIR}" "${package_manager}" "${cached_python_version}" requested_python_version python_version_origin @@ -159,15 +152,15 @@ meta_set "python_version_reason" "${python_version_origin}" # TODO: Add runtime.txt deprecation warning. case "${python_version_origin}" in default) - puts-step "No Python version was specified. Using the buildpack default: Python ${requested_python_version}" + output::step "No Python version was specified. Using the buildpack default: Python ${requested_python_version}" echo " To use a different version, see: https://devcenter.heroku.com/articles/python-runtimes" ;; cached) - puts-step "No Python version was specified. Using the same version as the last build: Python ${requested_python_version}" + output::step "No Python version was specified. Using the same version as the last build: Python ${requested_python_version}" echo " To use a different version, see: https://devcenter.heroku.com/articles/python-runtimes" ;; *) - puts-step "Using Python ${requested_python_version} specified in ${python_version_origin}" + output::step "Using Python ${requested_python_version} specified in ${python_version_origin}" ;; esac diff --git a/bin/detect b/bin/detect index 37d7ccdb..9f630329 100755 --- a/bin/detect +++ b/bin/detect @@ -45,7 +45,7 @@ done # 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 upfront. -display_error <&1 | tee "$COLLECTSTATIC_LOG" | sed '/^Post-processed/d;/^Copying/d;/^$/d' | indent +python "$MANAGE_FILE" collectstatic --noinput --traceback 2>&1 | tee "$COLLECTSTATIC_LOG" | sed '/^Post-processed/d;/^Copying/d;/^$/d' | output::indent COLLECTSTATIC_STATUS="${PIPESTATUS[0]}" set -e @@ -82,22 +87,28 @@ else meta_set "failure_reason" "collectstatic-other" fi -echo " ! Error while running '$ python $MANAGE_FILE collectstatic --noinput'." -echo " See traceback above for details." -echo -echo " You may need to update application code to resolve this error." -echo " Or, you can disable collectstatic for this application:" -echo -echo " $ heroku config:set DISABLE_COLLECTSTATIC=1" -echo -echo " https://devcenter.heroku.com/articles/django-assets" +output::error <<-EOF + Error: Unable to generate Django static files. + + The 'python ${MANAGE_FILE} collectstatic --noinput' Django + management command to generate static files failed. + + See the traceback above for details. + + You may need to update application code to resolve this error. + Or, you can disable collectstatic for this application: + + $ heroku config:set DISABLE_COLLECTSTATIC=1 + + https://devcenter.heroku.com/articles/django-assets +EOF # Additionally, dump out the environment, if debug mode is on. if [[ "${DEBUG_COLLECTSTATIC:-0}" == "1" ]]; then echo echo "****** Collectstatic environment variables:" echo - env | indent + env | output::indent fi exit 1 diff --git a/bin/steps/nltk b/bin/steps/nltk index c849285a..804e7a41 100755 --- a/bin/steps/nltk +++ b/bin/steps/nltk @@ -6,6 +6,7 @@ set -euo pipefail BUILDPACK_DIR=$(cd "$(dirname "$(dirname "$(dirname "${BASH_SOURCE[0]}")")")" && pwd) source "${BUILDPACK_DIR}/bin/utils" +source "${BUILDPACK_DIR}/lib/output.sh" # Required for `meta_set`. source "${BUILDPACK_DIR}/lib/metadata.sh" @@ -18,7 +19,7 @@ EXPORT_PATH="${BUILDPACK_DIR}/export" # Check that nltk was installed by pip, otherwise obviously not needed # shellcheck disable=SC2310 # TODO: This function is invoked in an 'if' condition so set -e will be disabled. if is_module_available 'nltk'; then - puts-step "Downloading NLTK corpora..." + output::step "Downloading NLTK corpora..." nltk_packages_definition="$BUILD_DIR/nltk.txt" @@ -26,14 +27,14 @@ if is_module_available 'nltk'; then meta_set "nltk_downloader" "enabled" readarray -t nltk_packages <"$nltk_packages_definition" - puts-step "Downloading NLTK packages: ${nltk_packages[*]}" + output::step "Downloading NLTK packages: ${nltk_packages[*]}" - python -m nltk.downloader -d "$BUILD_DIR/.heroku/python/nltk_data" "${nltk_packages[@]}" | indent + python -m nltk.downloader -d "$BUILD_DIR/.heroku/python/nltk_data" "${nltk_packages[@]}" | output::indent set_env NLTK_DATA "/app/.heroku/python/nltk_data" else meta_set "nltk_downloader" "skipped-no-nltk-file" - puts-warn "'nltk.txt' not found, not downloading any corpora" - puts-warn "Learn more: https://devcenter.heroku.com/articles/python-nltk" + echo " 'nltk.txt' not found, not downloading any corpora" + echo " Learn more: https://devcenter.heroku.com/articles/python-nltk" fi fi diff --git a/bin/steps/python b/bin/steps/python index e4b605df..37fbf6a3 100755 --- a/bin/steps/python +++ b/bin/steps/python @@ -17,7 +17,7 @@ PYTHON_URL="${S3_BASE_URL}/python-${python_full_version}-ubuntu-${UBUNTU_VERSION # 3. The user has pinned to an older buildpack version and the S3 bucket location or layout has changed since. # TODO: Update this message to be more specific once Python 3.8 support is dropped. if ! curl --output /dev/null --silent --head --fail --retry 3 --retry-connrefused --connect-timeout 10 "${PYTHON_URL}"; then - display_error <<-EOF + output::error <<-EOF Error: Python ${python_full_version} isn't available for this stack (${STACK}). For a list of the supported Python versions, see: @@ -38,10 +38,12 @@ function warn_if_patch_update_available() { # TODO: Update this message to suggest using the .python-version major version syntax to stay up to date, # once runtime.txt is deprecated and sticky-versioning only pins to the major version. if ((requested_patch_number < latest_patch_number)); then - puts-warn - puts-warn "A Python security update is available! Upgrade as soon as possible to: Python ${latest_patch_version}" - puts-warn "See: https://devcenter.heroku.com/articles/python-runtimes" - puts-warn + output::warning <<-EOF + Warning: A Python security update is available! + + Upgrade as soon as possible to: Python ${latest_patch_version} + See: https://devcenter.heroku.com/articles/python-runtimes + EOF meta_set "python_version_outdated" "true" else meta_set "python_version_outdated" "false" @@ -52,29 +54,31 @@ function warn_if_patch_update_available() { # if there weren't any errors with the version to avoid adding noise to the error messages. # TODO: Move this into lib/ as part of the warnings refactor. if [[ "${python_major_version}" == "3.8" ]]; then - puts-warn - puts-warn "Python 3.8 will reach its upstream end-of-life in October 2024, at which" - puts-warn "point it will no longer receive security updates:" - puts-warn "https://devguide.python.org/versions/#supported-versions" - puts-warn - puts-warn "Support for Python 3.8 will be removed from this buildpack on December 4th, 2024." - puts-warn - puts-warn "Upgrade to a newer Python version as soon as possible to keep your app secure." - puts-warn "See: https://devcenter.heroku.com/articles/python-runtimes" - puts-warn + output::warning <<-'EOF' + Warning: Support for Python 3.8 is ending soon! + + Python 3.8 will reach its upstream end-of-life in October 2024, at which + point it will no longer receive security updates: + https://devguide.python.org/versions/#supported-versions + + Support for Python 3.8 will be removed from this buildpack on December 4th, 2024. + + Upgrade to a newer Python version as soon as possible to keep your app secure. + See: https://devcenter.heroku.com/articles/python-runtimes + EOF fi warn_if_patch_update_available "${python_full_version}" "${python_major_version}" if [[ "$STACK" != "$CACHED_PYTHON_STACK" ]]; then - puts-step "Stack has changed from $CACHED_PYTHON_STACK to $STACK, clearing cache" + output::step "Stack has changed from $CACHED_PYTHON_STACK to $STACK, clearing cache" rm -rf .heroku/python-stack .heroku/python-version .heroku/python .heroku/vendor .heroku/python .heroku/python-sqlite3-version fi # TODO: Clean this up as part of the cache refactor. if [[ -f .heroku/python-version ]]; then if [[ "${cached_python_version}" != "${python_full_version}" ]]; then - puts-step "Python version has changed from ${cached_python_version} to ${python_full_version}, clearing cache" + output::step "Python version has changed from ${cached_python_version} to ${python_full_version}, clearing cache" rm -rf .heroku/python else SKIP_INSTALL=1 @@ -92,22 +96,22 @@ if [[ -f "${BUILD_DIR}/requirements.txt" ]]; then else # IF there IS a cached directory, check for differences with the new one if ! diff "$BUILD_DIR/requirements.txt" "$CACHE_DIR/.heroku/requirements.txt" &>/dev/null; then - puts-step "Requirements file has been changed, clearing cached dependencies" + output::step "Requirements file has been changed, clearing cached dependencies" # if there are any differences, clear the Python cache # Installing Python over again does not take noticably more time cp -R "$BUILD_DIR/requirements.txt" "$CACHE_DIR/.heroku/requirements.txt" rm -rf .heroku/python unset SKIP_INSTALL else - puts-step "No change in requirements detected, installing from cache" + output::step "No change in requirements detected, installing from cache" fi fi fi if [[ "${SKIP_INSTALL:-0}" == "1" ]]; then - puts-step "Using cached install of Python ${python_full_version}" + output::step "Using cached install of Python ${python_full_version}" else - puts-step "Installing Python ${python_full_version}" + output::step "Installing Python ${python_full_version}" # Prepare destination directory. mkdir -p .heroku/python @@ -115,7 +119,12 @@ else if ! curl --silent --show-error --fail --retry 3 --retry-connrefused --connect-timeout 10 "${PYTHON_URL}" | tar --zstd --extract --directory .heroku/python; then # The Python version was confirmed to exist previously, so any failure here is due to # a networking issue or archive/buildpack bug rather than the runtime not existing. - display_error "Error: Failed to download/install Python ${python_full_version}." + output::error <<-EOF + Error: Failed to download/install Python ${python_full_version}. + + In some cases, this happens due to an unstable network connection. + Please try again and to see if the error resolves itself. + EOF meta_set "failure_reason" "python-download" exit 1 fi diff --git a/bin/steps/sqlite3 b/bin/steps/sqlite3 index a174fa2b..3ef119a3 100755 --- a/bin/steps/sqlite3 +++ b/bin/steps/sqlite3 @@ -63,7 +63,7 @@ sqlite3_install() { } buildpack_sqlite3_install() { - puts-step "Installing SQLite3" + output::step "Installing SQLite3" # TODO: This never actual prints failure or even aborts the build, since # the conditional disables `set -e` inside the called function: diff --git a/bin/utils b/bin/utils index 45ab374d..c2b25254 100755 --- a/bin/utils +++ b/bin/utils @@ -10,26 +10,11 @@ source "${BUILDPACK_DIR:?}/vendor/buildpack-stdlib_v8.sh" sed() { command sed -u "$@"; } -# Syntax sugar. -indent() { - sed "s/^/ /" -} - # Clean up pip output cleanup() { sed -e 's/\.\.\.\+/.../g' | sed -e '/already satisfied/Id' | sed -e '/No files were found to uninstall/Id' | sed -e '/Overwriting/Id' | sed -e '/python executable/Id' | sed -e '/no previously-included files/Id' } -# Buildpack Steps. -puts-step() { - echo "-----> $*" -} - -# Buildpack Warnings. -puts-warn() { - echo " ! $*" -} - # Does some serious copying. deep-cp() { declare source="$1" target="$2" diff --git a/bin/warnings b/bin/warnings index e24d41ea..55cb9b6f 100755 --- a/bin/warnings +++ b/bin/warnings @@ -3,10 +3,12 @@ gdal-missing() { # shellcheck disable=SC2154 # TODO: Env var is referenced but not assigned. if grep -qi 'Could not find gdal-config' "${WARNINGS_LOG}"; then - echo - puts-warn "Hello! Package installation failed since the GDAL library was not found." - puts-warn "For GDAL, GEOS and PROJ support, use the Geo buildpack alongside the Python buildpack:" - puts-warn "https://github.com/heroku/heroku-geo-buildpack" + output::error <<-'EOF' + Error: Package installation failed since the GDAL library was not found. + + For GDAL, GEOS and PROJ support, use the Geo buildpack alongside the Python buildpack: + https://github.com/heroku/heroku-geo-buildpack + EOF fi } diff --git a/lib/output.sh b/lib/output.sh index 4141e1a2..a94074a1 100644 --- a/lib/output.sh +++ b/lib/output.sh @@ -4,21 +4,62 @@ # however, it helps Shellcheck realise the options under which these functions will run. set -euo pipefail -# TODO: Switch this file to using namespaced functions like `output::`. - ANSI_RED='\033[1;31m' +ANSI_YELLOW='\033[1;33m' ANSI_RESET='\033[0m' -# shellcheck disable=SC2120 # Prevent warnings about unused arguments due to the split args vs stdin API. -function display_error() { - # Send all output to stderr - exec 1>&2 - # If arguments are given, redirect them to stdin. This allows the function - # to be invoked with either a string argument or stdin (e.g. via <<-EOF). - (($#)) && exec <<<"${@}" - echo +# Output a single line step message to stdout. +# +# Usage: +# ``` +# output::step "Installing Python ..." +# ``` +function output::step() { + echo "-----> ${1}" +} + +# Indent passed stdout. Typically used to indent command output within a step. +# +# Usage: +# ``` +# pip install ... | output::indent +# ``` +function output::indent() { + sed --unbuffered "s/^/ /" +} + +# Output a styled multi-line warning message to stderr. +# +# Usage: +# ``` +# output::warning <<-EOF +# Warning: The warning summary. +# +# Detailed description. +# EOF +# ``` +function output::warning() { + echo >&2 + while IFS= read -r line; do + echo -e "${ANSI_YELLOW} ! ${line}${ANSI_RESET}" >&2 + done + echo >&2 +} + +# Output a styled multi-line error message to stderr. +# +# Usage: +# ``` +# output::error <<-EOF +# Error: The error summary. +# +# Detailed description. +# EOF +# ``` +function output::error() { + echo >&2 while IFS= read -r line; do - echo -e "${ANSI_RED} ! ${line}${ANSI_RESET}" + echo -e "${ANSI_RED} ! ${line}${ANSI_RESET}" >&2 done - echo + echo >&2 } diff --git a/lib/package_manager.sh b/lib/package_manager.sh index 544ea238..4c9fdd7f 100644 --- a/lib/package_manager.sh +++ b/lib/package_manager.sh @@ -13,6 +13,12 @@ function package_manager::determine_package_manager() { meta_set "pipenv_has_lockfile" "true" elif [[ -f "${build_dir}/Pipfile" ]]; then # TODO: Start requiring a Pipfile.lock and make this branch a "missing lockfile" error instead. + # TODO: Adjust this warning to mention support for missing Pipfile.lock will be removed soon. + output::warning <<-'EOF' + Warning: No 'Pipfile.lock' found! + + We recommend you commit this into your repository. + EOF package_managers_found+=(pipenv) meta_set "pipenv_has_lockfile" "false" fi @@ -36,7 +42,7 @@ function package_manager::determine_package_manager() { return 0 ;; 0) - display_error <<-EOF + output::error <<-EOF Error: Couldn't find any supported Python package manager files. A Python app on Heroku must have either a 'requirements.txt' or diff --git a/lib/pip.sh b/lib/pip.sh index c8f55473..eb4535a2 100644 --- a/lib/pip.sh +++ b/lib/pip.sh @@ -39,14 +39,14 @@ function pip::install_pip_setuptools_wheel() { packages_display_text+=", setuptools ${SETUPTOOLS_VERSION} and wheel ${WHEEL_VERSION}" fi - puts-step "Installing ${packages_display_text}" + output::step "Installing ${packages_display_text}" /app/.heroku/python/bin/python "${bundled_pip_module_path}" install --quiet --disable-pip-version-check --no-cache-dir \ "${packages_to_install[@]}" } function pip::install_dependencies() { - puts-step "Installing requirements with pip" + output::step "Installing requirements with pip" # Make select pip config vars set on the Heroku app available to pip. # TODO: Expose all config vars (after suitable checks are added for unsafe env vars) @@ -68,7 +68,7 @@ function pip::install_dependencies() { set +e # shellcheck disable=SC2154 # TODO: Env var is referenced but not assigned. - /app/.heroku/python/bin/pip install "${args[@]}" --exists-action=w --src='/app/.heroku/src' --disable-pip-version-check --no-cache-dir --progress-bar off 2>&1 | tee "${WARNINGS_LOG}" | cleanup | indent + /app/.heroku/python/bin/pip install "${args[@]}" --exists-action=w --src='/app/.heroku/src' --disable-pip-version-check --no-cache-dir --progress-bar off 2>&1 | tee "${WARNINGS_LOG}" | cleanup | output::indent local PIP_STATUS="${PIPESTATUS[0]}" set -e @@ -82,8 +82,8 @@ function pip::install_dependencies() { # Install test dependencies, for Heroku CI. if [[ "${INSTALL_TEST:-0}" == "1" ]]; then if [[ -f requirements-test.txt ]]; then - puts-step "Installing test dependencies..." - /app/.heroku/python/bin/pip install -r requirements-test.txt --exists-action=w --src='/app/.heroku/src' --disable-pip-version-check --no-cache-dir 2>&1 | cleanup | indent + output::step "Installing test dependencies..." + /app/.heroku/python/bin/pip install -r requirements-test.txt --exists-action=w --src='/app/.heroku/src' --disable-pip-version-check --no-cache-dir 2>&1 | cleanup | output::indent fi fi } diff --git a/lib/pipenv.sh b/lib/pipenv.sh index f718a7d2..bfe0032d 100644 --- a/lib/pipenv.sh +++ b/lib/pipenv.sh @@ -12,7 +12,7 @@ function pipenv::install_pipenv() { PIPENV_VERSION=$(get_requirement_version 'pipenv') meta_set "pipenv_version" "${PIPENV_VERSION}" - puts-step "Installing Pipenv ${PIPENV_VERSION}" + output::step "Installing Pipenv ${PIPENV_VERSION}" # TODO: Install Pipenv into a venv so it isn't leaked into the app environment. # TODO: Explore viability of making Pipenv only be available during the build, to reduce slug size. @@ -38,16 +38,16 @@ function pipenv::install_dependencies() { # Install the test dependencies, for CI. # TODO: This is currently inconsistent with the non-test path, since it assumes (but doesn't check for) a lockfile. if [[ "${INSTALL_TEST:-0}" == "1" ]]; then - puts-step "Installing test dependencies with Pipenv" - /app/.heroku/python/bin/pipenv install --dev --system --deploy --extra-pip-args='--src=/app/.heroku/src' 2>&1 | cleanup | indent + output::step "Installing test dependencies with Pipenv" + /app/.heroku/python/bin/pipenv install --dev --system --deploy --extra-pip-args='--src=/app/.heroku/src' 2>&1 | cleanup | output::indent # Install the dependencies. elif [[ ! -f Pipfile.lock ]]; then - puts-step "Installing dependencies with Pipenv" - /app/.heroku/python/bin/pipenv install --system --skip-lock --extra-pip-args='--src=/app/.heroku/src' 2>&1 | indent + output::step "Installing dependencies with Pipenv" + /app/.heroku/python/bin/pipenv install --system --skip-lock --extra-pip-args='--src=/app/.heroku/src' 2>&1 | output::indent else - puts-step "Installing dependencies with Pipenv" - /app/.heroku/python/bin/pipenv install --system --deploy --extra-pip-args='--src=/app/.heroku/src' 2>&1 | indent + output::step "Installing dependencies with Pipenv" + /app/.heroku/python/bin/pipenv install --system --deploy --extra-pip-args='--src=/app/.heroku/src' 2>&1 | output::indent fi } diff --git a/lib/python_version.sh b/lib/python_version.sh index aa4df353..bd52223f 100644 --- a/lib/python_version.sh +++ b/lib/python_version.sh @@ -96,7 +96,7 @@ function python_version::parse_runtime_txt() { local version="${BASH_REMATCH[1]}" echo "${version}" else - display_error <<-EOF + output::error <<-EOF Error: Invalid Python version in runtime.txt. The Python version specified in 'runtime.txt' isn't in @@ -140,7 +140,7 @@ function python_version::parse_python_version_file() { echo "${version}" return 0 else - display_error <<-EOF + output::error <<-EOF Error: Invalid Python version in .python-version. The Python version specified in '.python-version' isn't in @@ -165,7 +165,7 @@ function python_version::parse_python_version_file() { fi ;; 0) - display_error <<-EOF + output::error <<-EOF Error: Invalid Python version in .python-version. No Python version was found in the '.python-version' file. @@ -180,7 +180,7 @@ function python_version::parse_python_version_file() { return 1 ;; *) - display_error <<-EOF + output::error <<-EOF Error: Invalid Python version in .python-version. Multiple Python versions were found in the '.python-version' @@ -218,7 +218,7 @@ function python_version::read_pipenv_python_version() { fi if ! version=$(jq --raw-output '._meta.requires.python_full_version // ._meta.requires.python_version' "${pipfile_lock_path}" 2>&1); then - display_error <<-EOF + output::error <<-EOF Error: Can't parse Pipfile.lock. A Pipfile.lock file was found, however, it couldn't be parsed: @@ -243,7 +243,7 @@ function python_version::read_pipenv_python_version() { if [[ "${version}" =~ ^${PYTHON_VERSION_REGEX}$ ]]; then echo "${version}" else - display_error <<-EOF + output::error <<-EOF Error: Invalid Python version in Pipfile / Pipfile.lock. The Python version specified in Pipfile / Pipfile.lock by the @@ -283,7 +283,7 @@ function python_version::resolve_python_version() { if ((major < 3 || (major == 3 && minor < 8))); then if [[ "${python_version_origin}" == "cached" ]]; then - display_error <<-EOF + output::error <<-EOF Error: The cached Python version has reached end-of-life. Your app doesn't specify a Python version, and so normally @@ -303,7 +303,7 @@ function python_version::resolve_python_version() { https://devcenter.heroku.com/articles/python-support#supported-runtimes EOF else - display_error <<-EOF + output::error <<-EOF Error: The requested Python version has reached end-of-life. Python ${major}.${minor} has reached its upstream end-of-life, and is @@ -325,7 +325,7 @@ function python_version::resolve_python_version() { if (((major == 3 && minor > 13) || major >= 4)); then if [[ "${python_version_origin}" == "cached" ]]; then - display_error <<-EOF + output::error <<-EOF Error: The cached Python version isn't recognised. Your app doesn't specify a Python version, and so normally @@ -340,7 +340,7 @@ function python_version::resolve_python_version() { Please switch back to a newer version of this buildpack. EOF else - display_error <<-EOF + output::error <<-EOF Error: The requested Python version isn't recognised. The requested Python version ${major}.${minor} isn't recognised. diff --git a/lib/utils.sh b/lib/utils.sh index 7d702c54..c64bbfc7 100644 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -18,7 +18,7 @@ function utils::bundled_pip_module_path() { local bundled_pip_wheel="${bundled_pip_wheel_list[0]}" if [[ -z "${bundled_pip_wheel}" ]]; then - display_error "Error: Failed to locate the bundled pip wheel." + output::error "Error: Failed to locate the bundled pip wheel." meta_set "failure_reason" "bundled-pip-not-found" return 1 fi @@ -28,7 +28,7 @@ function utils::bundled_pip_module_path() { function utils::abort_internal_error() { local message="${1}" - display_error "Internal error: ${message} (line $(caller || true))." + output::error "Internal error: ${message} (line $(caller || true))." meta_set "failure_reason" "internal-error" exit 1 } diff --git a/spec/hatchet/django_spec.rb b/spec/hatchet/django_spec.rb index bd7feb1a..38f7e3e0 100644 --- a/spec/hatchet/django_spec.rb +++ b/spec/hatchet/django_spec.rb @@ -82,7 +82,11 @@ app.deploy do |app| expect(clean_output(app.output)).to include(<<~OUTPUT) remote: -----> Skipping Django collectstatic since the file '.heroku/collectstatic_disabled' exists. - remote: ! This approach is deprecated, please set the env var DISABLE_COLLECTSTATIC=1 instead. + remote: + remote: ! Warning: The .heroku/collectstatic_disabled file is deprecated. + remote: ! + remote: ! Please remove the file and set the env var DISABLE_COLLECTSTATIC=1 instead. + remote: OUTPUT expect(app.output).not_to include('manage.py collectstatic') end @@ -100,15 +104,21 @@ remote: .+ remote: ModuleNotFoundError: No module named 'gettingstarted' remote: - remote: ! Error while running '\\$ python manage.py collectstatic --noinput'. - remote: See traceback above for details. - remote: - remote: You may need to update application code to resolve this error. - remote: Or, you can disable collectstatic for this application: remote: - remote: \\$ heroku config:set DISABLE_COLLECTSTATIC=1 + remote: ! Error: Unable to generate Django static files. + remote: ! + remote: ! The 'python manage.py collectstatic --noinput' Django + remote: ! management command to generate static files failed. + remote: ! + remote: ! See the traceback above for details. + remote: ! + remote: ! You may need to update application code to resolve this error. + remote: ! Or, you can disable collectstatic for this application: + remote: ! + remote: ! \\$ heroku config:set DISABLE_COLLECTSTATIC=1 + remote: ! + remote: ! https://devcenter.heroku.com/articles/django-assets remote: - remote: https://devcenter.heroku.com/articles/django-assets remote: ! Push rejected, failed to compile Python app. REGEX end diff --git a/spec/hatchet/nltk_spec.rb b/spec/hatchet/nltk_spec.rb index 0ed2a167..28b1ab66 100644 --- a/spec/hatchet/nltk_spec.rb +++ b/spec/hatchet/nltk_spec.rb @@ -32,8 +32,8 @@ app.deploy do |app| expect(clean_output(app.output)).to match(Regexp.new(<<~REGEX)) remote: -----> Downloading NLTK corpora... - remote: ! 'nltk.txt' not found, not downloading any corpora - remote: ! Learn more: https://devcenter.heroku.com/articles/python-nltk + remote: 'nltk.txt' not found, not downloading any corpora + remote: Learn more: https://devcenter.heroku.com/articles/python-nltk REGEX end end diff --git a/spec/hatchet/pip_spec.rb b/spec/hatchet/pip_spec.rb index 59b2ba0c..0a4cbec5 100644 --- a/spec/hatchet/pip_spec.rb +++ b/spec/hatchet/pip_spec.rb @@ -261,9 +261,13 @@ it 'outputs instructions for how to resolve the build failure' do app.deploy do |app| expect(clean_output(app.output)).to include(<<~OUTPUT) - remote: ! Hello! Package installation failed since the GDAL library was not found. + remote: note: This error originates from a subprocess, and is likely not a problem with pip. + remote: + remote: ! Error: Package installation failed since the GDAL library was not found. + remote: ! remote: ! For GDAL, GEOS and PROJ support, use the Geo buildpack alongside the Python buildpack: remote: ! https://github.com/heroku/heroku-geo-buildpack + remote: remote: ! Push rejected, failed to compile Python app. OUTPUT end diff --git a/spec/hatchet/pipenv_spec.rb b/spec/hatchet/pipenv_spec.rb index 8abe6fb6..02686acc 100644 --- a/spec/hatchet/pipenv_spec.rb +++ b/spec/hatchet/pipenv_spec.rb @@ -83,10 +83,12 @@ expect(clean_output(app.output)).to match(Regexp.new(<<~REGEX)) remote: -----> Python app detected remote: -----> Using Python 3.9.0 specified in Pipfile.lock + remote: + remote: ! Warning: A Python security update is available! remote: ! - remote: ! A Python security update is available! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_9} + remote: ! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_9} remote: ! See: https://devcenter.heroku.com/articles/python-runtimes - remote: ! + remote: remote: -----> Installing Python 3.9.0 remote: -----> Installing pip #{PIP_VERSION}, setuptools #{SETUPTOOLS_VERSION} and wheel #{WHEEL_VERSION} remote: -----> Installing Pipenv #{PIPENV_VERSION} @@ -143,7 +145,11 @@ app.deploy do |app| expect(clean_output(app.output)).to match(Regexp.new(<<~REGEX, Regexp::MULTILINE)) remote: -----> Python app detected - remote: ! No 'Pipfile.lock' found! We recommend you commit this into your repository. + remote: + remote: ! Warning: No 'Pipfile.lock' found! + remote: ! + remote: ! We recommend you commit this into your repository. + remote: remote: -----> No Python version was specified. Using the buildpack default: Python #{DEFAULT_PYTHON_MAJOR_VERSION} remote: To use a different version, see: https://devcenter.heroku.com/articles/python-runtimes remote: -----> Installing Python #{DEFAULT_PYTHON_FULL_VERSION} diff --git a/spec/hatchet/python_update_warning_spec.rb b/spec/hatchet/python_update_warning_spec.rb index 664c15bc..c10e5c28 100644 --- a/spec/hatchet/python_update_warning_spec.rb +++ b/spec/hatchet/python_update_warning_spec.rb @@ -16,6 +16,8 @@ expect(clean_output(app.output)).to include(<<~OUTPUT) remote: -----> Python app detected remote: -----> Using Python 3.8.0 specified in runtime.txt + remote: + remote: ! Warning: Support for Python 3.8 is ending soon! remote: ! remote: ! Python 3.8 will reach its upstream end-of-life in October 2024, at which remote: ! point it will no longer receive security updates: @@ -25,11 +27,13 @@ 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: ! Warning: A Python security update is available! remote: ! - remote: ! - remote: ! A Python security update is available! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_8} + remote: ! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_8} remote: ! See: https://devcenter.heroku.com/articles/python-runtimes - remote: ! + remote: remote: -----> Installing Python 3.8.0 OUTPUT end @@ -66,10 +70,12 @@ expect(clean_output(app.output)).to include(<<~OUTPUT) remote: -----> Python app detected remote: -----> Using Python 3.9.0 specified in .python-version + remote: + remote: ! Warning: A Python security update is available! remote: ! - remote: ! A Python security update is available! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_9} + remote: ! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_9} remote: ! See: https://devcenter.heroku.com/articles/python-runtimes - remote: ! + remote: remote: -----> Installing Python 3.9.0 OUTPUT end diff --git a/spec/hatchet/python_version_spec.rb b/spec/hatchet/python_version_spec.rb index ec6313d3..21514880 100644 --- a/spec/hatchet/python_version_spec.rb +++ b/spec/hatchet/python_version_spec.rb @@ -63,10 +63,12 @@ remote: -----> Python app detected remote: -----> No Python version was specified. Using the same version as the last build: Python 3.12.3 remote: To use a different version, see: https://devcenter.heroku.com/articles/python-runtimes + remote: + remote: ! Warning: A Python security update is available! remote: ! - remote: ! A Python security update is available! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_12} + remote: ! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_12} remote: ! See: https://devcenter.heroku.com/articles/python-runtimes - remote: ! + remote: remote: -----> No change in requirements detected, installing from cache remote: -----> Using cached install of Python 3.12.3 OUTPUT @@ -86,6 +88,8 @@ expect(clean_output(app.output)).to include(<<~OUTPUT) remote: -----> Python app detected remote: -----> Using Python 3.8 specified in .python-version + remote: + remote: ! Warning: Support for Python 3.8 is ending soon! remote: ! remote: ! Python 3.8 will reach its upstream end-of-life in October 2024, at which remote: ! point it will no longer receive security updates: @@ -95,7 +99,7 @@ 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: -----> Installing Python #{LATEST_PYTHON_3_8} remote: -----> Installing pip #{PIP_VERSION}, setuptools #{SETUPTOOLS_VERSION} and wheel #{WHEEL_VERSION} remote: -----> Installing SQLite3 diff --git a/spec/hatchet/stack_spec.rb b/spec/hatchet/stack_spec.rb index 3f55f533..9c84e5d1 100644 --- a/spec/hatchet/stack_spec.rb +++ b/spec/hatchet/stack_spec.rb @@ -24,10 +24,12 @@ remote: -----> Python app detected remote: -----> No Python version was specified. Using the same version as the last build: Python 3.12.3 remote: To use a different version, see: https://devcenter.heroku.com/articles/python-runtimes + remote: + remote: ! Warning: A Python security update is available! remote: ! - remote: ! A Python security update is available! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_12} + remote: ! Upgrade as soon as possible to: Python #{LATEST_PYTHON_3_12} remote: ! See: https://devcenter.heroku.com/articles/python-runtimes - remote: ! + remote: remote: -----> Stack has changed from heroku-22 to heroku-24, clearing cache remote: -----> No change in requirements detected, installing from cache remote: -----> Installing Python 3.12.3