From 032151004d354383979ca6453251732f86d915cd Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:53:54 +0000 Subject: [PATCH] Improve the error message when the build data file has been deleted On Honeycomb I've seen cases of builds failing due to a pre/post-compile hook having deleted the buildpacks internal build data file - typically as part of the user trying to clean up the build cache. This file is required for the buildpack to function correctly, so deleting it isn't supported. However, we can show a clearer error message. Before: ``` -----> Running bin/post_compile hook + rm -rf /tmp/cache jq: error: Could not open file /tmp/cache/build-data/python.json: No such file or directory jq: error: Could not open file /tmp/cache/build-data/python.json: No such file or directory ! Internal Error: An unhandled buildpack error occurred. ! ! An unhandled error occurred while executing the command: ! jq --exit-status --arg key "${key}" "${jq_args[@]}" '. + { ($key): ($value) }' "${BUILD_DATA_FILE}" ! ! If this issue persists, please open a support ticket or file ! an issue on the buildpack's GitHub repository: ! https://help.heroku.com/ ! https://github.com/heroku/heroku-buildpack-python/issues ! ! Stack trace: ! build_data::_set @ /tmp/buildpack/lib/build_data.sh:109 ! build_data::set_raw @ /tmp/buildpack/lib/build_data.sh:76 ! build_data::set_duration @ /tmp/buildpack/lib/build_data.sh:60 ! hooks::run_hook @ /tmp/buildpack/lib/hooks.sh:37 ! main @ ./bin/compile:289 jq: error: Could not open file /tmp/cache/build-data/python.json: No such file or directory jq: error: Could not open file /tmp/cache/build-data/python.json: No such file or directory ! Internal Error: An unhandled buildpack error occurred. ! ! An unhandled error occurred while executing the command: ! new_data_file_contents="$(jq --exit-status --arg key "${key}" "${jq_args[@]}" '. + { ($key): ($value) }' "${BUILD_DATA_FILE}")" ! ! If this issue persists, please open a support ticket or file ! an issue on the buildpack's GitHub repository: ! https://help.heroku.com/ ! https://github.com/heroku/heroku-buildpack-python/issues ! ! Stack trace: ! build_data::_set @ /tmp/buildpack/lib/build_data.sh:109 ! build_data::set_raw @ /tmp/buildpack/lib/build_data.sh:76 ! build_data::set_duration @ /tmp/buildpack/lib/build_data.sh:60 ! hooks::run_hook @ /tmp/buildpack/lib/hooks.sh:37 ! main @ ./bin/compile:289 jq: error: Could not open file /tmp/cache/build-data/python.json: No such file or directory ``` After: ``` -----> Running bin/post_compile hook + rm -rf /tmp/cache ! Error: The buildpack's build data file has been deleted. ! ! The Python buildpack's build data file can't be found: ! /tmp/cache/build-data/python.json ! ! This file is required for the buildpack to work correctly, ! and so you must not delete it when removing files from the ! build cache or /tmp directories. ``` Lastly, we also now use `--exit-status` in more places to prevent invalid empty build data files from being silently ignored. GUS-W-20220731. --- CHANGELOG.md | 1 + lib/build_data.sh | 25 ++++++++++++++++--- .../hooks_delete_cache_dir/bin/post_compile | 6 +++++ .../hooks_delete_cache_dir/requirements.txt | 0 spec/hatchet/hooks_spec.rb | 24 ++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 spec/fixtures/hooks_delete_cache_dir/bin/post_compile create mode 100644 spec/fixtures/hooks_delete_cache_dir/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index b3417b0e8..6dd2ef56f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Updated uv from 0.9.7 to 0.9.9. ([#1961](https://github.com/heroku/heroku-buildpack-python/pull/1961)) - Improved the error message shown for `.python-version` files that contain unexpected ASCII control code characters. ([#1962](https://github.com/heroku/heroku-buildpack-python/pull/1962)) - Fixed Bash command substitution warnings from being shown if `runtime.txt` contains null byte characters. ([#1962](https://github.com/heroku/heroku-buildpack-python/pull/1962)) +- Improved the error message shown if the buildpack's build data file is deleted by a pre/post-compile hook. ([#1963](https://github.com/heroku/heroku-buildpack-python/pull/1963)) ## [v318] - 2025-11-12 diff --git a/lib/build_data.sh b/lib/build_data.sh index 9841e0f45..93434ac38 100644 --- a/lib/build_data.sh +++ b/lib/build_data.sh @@ -104,9 +104,25 @@ function build_data::_set() { local jq_args=(--argjson value "${value}") fi - local new_data_file_contents - new_data_file_contents="$(jq --arg key "${key}" "${jq_args[@]}" '. + { ($key): ($value) }' "${BUILD_DATA_FILE}")" - echo "${new_data_file_contents}" >"${BUILD_DATA_FILE}" + if [[ -f "${BUILD_DATA_FILE}" ]]; then + local new_data_file_contents + new_data_file_contents="$(jq --exit-status --arg key "${key}" "${jq_args[@]}" '. + { ($key): ($value) }' "${BUILD_DATA_FILE}")" + echo "${new_data_file_contents}" >"${BUILD_DATA_FILE}" + else + output::error <<-EOF + Error: Can't find the buildpack's build data file. + + The Python buildpack's internal build data file is missing: + ${BUILD_DATA_FILE} + + This file is required for the buildpack to work correctly, + and so you must not delete it when removing files from the + build cache or /tmp directories. + EOF + build_data::setup + build_data::set_string "failure_reason" "build-data::data-file-deleted" + exit 1 + fi } # Check whether an entry exists in the build data store for the current build. @@ -143,6 +159,7 @@ function build_data::get_previous() { tac "${LEGACY_BUILD_DATA_FILE}" | { grep --perl-regexp --only-matching --max-count=1 "^${key}=\K.*$" || true; } elif [[ -f "${PREVIOUS_BUILD_DATA_FILE}" ]]; then # The `// empty` ensures we return the empty string rather than `null` if the key doesn't exist. + # We don't use `--exit-status` since `false` is a valid value for us to retrieve. jq --raw-output ".${key} // empty" "${PREVIOUS_BUILD_DATA_FILE}" fi } @@ -170,5 +187,5 @@ function build_data::current_unix_realtime() { # build_data::print_bin_report_json # ``` function build_data::print_bin_report_json() { - jq --sort-keys '.' "${BUILD_DATA_FILE}" + jq --exit-status --sort-keys '.' "${BUILD_DATA_FILE}" } diff --git a/spec/fixtures/hooks_delete_cache_dir/bin/post_compile b/spec/fixtures/hooks_delete_cache_dir/bin/post_compile new file mode 100644 index 000000000..383d0656b --- /dev/null +++ b/spec/fixtures/hooks_delete_cache_dir/bin/post_compile @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail + +set -x +rm -rf "${CACHE_DIR:?}" diff --git a/spec/fixtures/hooks_delete_cache_dir/requirements.txt b/spec/fixtures/hooks_delete_cache_dir/requirements.txt new file mode 100644 index 000000000..e69de29bb diff --git a/spec/hatchet/hooks_spec.rb b/spec/hatchet/hooks_spec.rb index e58a7969c..18fa2eed6 100644 --- a/spec/hatchet/hooks_spec.rb +++ b/spec/hatchet/hooks_spec.rb @@ -78,4 +78,28 @@ end end end + + context 'when an app tries to delete the whole cache directory including the build data file' do + let(:app) { Hatchet::Runner.new('spec/fixtures/hooks_delete_cache_dir', allow_failure: true) } + + it 'aborts the build with a suitable error message' do + app.deploy do |app| + expect(clean_output(app.output)).to include(<<~OUTPUT) + remote: -----> Running bin/post_compile hook + remote: + rm -rf /tmp/codon/tmp/cache + remote: + remote: ! Error: Can't find the buildpack's build data file. + remote: ! + remote: ! The Python buildpack's internal build data file is missing: + remote: ! /tmp/codon/tmp/cache/build-data/python.json + remote: ! + remote: ! This file is required for the buildpack to work correctly, + remote: ! and so you must not delete it when removing files from the + remote: ! build cache or /tmp directories. + remote: + remote: ! Push rejected, failed to compile Python app. + OUTPUT + end + end + end end