Skip to content

Commit bf2ce13

Browse files
authored
Improve error handling and log output of {pre,post}_compile hooks (#1667)
* If the hooks fail, there is now an explicit error message (plus metrics event). * The build log step now references the full script location to aid. with understanding of what's happening. * Any output of the hooks is now indented to aid readability. * The hooks implementation has been refactored and moved to `lib/` as part of the overall buildpack reorganisation/refactoring. * Test coverage has been added for the failing hooks cases (previously only the successful hooks scenario was tested). (This is one step towards ensuring all buildpack failure cases have an explicit error message and a metrics `failure_reason`.) GUS-W-8059892.
1 parent cbe99d6 commit bf2ce13

File tree

13 files changed

+217
-136
lines changed

13 files changed

+217
-136
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44

55
- Updated buildpack-generated warning messages to use colour and be more consistently formatted. ([#1666](https://github.com/heroku/heroku-buildpack-python/pull/1666))
6+
- Improved build log output and error messages for the `bin/pre_compile` and `bin/post_compile` customisation hooks. ([#1667](https://github.com/heroku/heroku-buildpack-python/pull/1667))
67

78
## [v261] - 2024-10-14
89

bin/compile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ENV_DIR="${3}"
1919
BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd)
2020

2121
source "${BUILDPACK_DIR}/bin/utils"
22+
source "${BUILDPACK_DIR}/lib/hooks.sh"
2223
source "${BUILDPACK_DIR}/lib/metadata.sh"
2324
source "${BUILDPACK_DIR}/lib/output.sh"
2425
source "${BUILDPACK_DIR}/lib/package_manager.sh"
@@ -119,7 +120,7 @@ if [[ -d "$CACHE_DIR/.heroku/src" ]]; then
119120
fi
120121

121122
# Runs a `bin/pre_compile` script if found in the app source, allowing build customisation.
122-
source "${BUILDPACK_DIR}/bin/steps/hooks/pre_compile"
123+
hooks::run_hook "pre_compile"
123124

124125
# TODO: Clear the cache if this isn't a valid version, as part of the cache refactor.
125126
# (Currently the version is instead validated in `read_requested_python_version()`)
@@ -297,7 +298,7 @@ cp "${BUILDPACK_DIR}/vendor/WEB_CONCURRENCY.sh" "$WEB_CONCURRENCY_PROFILE_PATH"
297298
cp "${BUILDPACK_DIR}/vendor/python.gunicorn.sh" "$GUNICORN_PROFILE_PATH"
298299

299300
# Runs a `bin/post_compile` script if found in the app source, allowing build customisation.
300-
source "${BUILDPACK_DIR}/bin/steps/hooks/post_compile"
301+
hooks::run_hook "post_compile"
301302

302303
# Store new artifacts in the cache.
303304
rm -rf "$CACHE_DIR/.heroku/python"

bin/steps/hooks/post_compile

Lines changed: 0 additions & 12 deletions
This file was deleted.

bin/steps/hooks/pre_compile

Lines changed: 0 additions & 12 deletions
This file was deleted.

lib/hooks.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
3+
# This is technically redundant, since all consumers of this lib will have enabled these,
4+
# however, it helps Shellcheck realise the options under which these functions will run.
5+
set -euo pipefail
6+
7+
# Used to run the `bin/pre_compile` and `bin/post_compile`s scripts if found in the app source,
8+
# allowing for build customisation.
9+
function hooks::run_hook() {
10+
local hook_name="${1}"
11+
local script_path="bin/${hook_name}"
12+
13+
if [[ -f "${script_path}" ]]; then
14+
local hook_start_time
15+
hook_start_time=$(nowms)
16+
output::step "Running ${script_path} hook"
17+
meta_set "${hook_name}_hook" "true"
18+
chmod +x "${script_path}"
19+
20+
# shellcheck disable=SC2310 # This function is invoked in an 'if' condition so set -e will be disabled.
21+
if ! sub_env "${script_path}" |& output::indent; then
22+
output::error <<-EOF
23+
Error: Failed to run the ${script_path} script.
24+
25+
We found a '${script_path}' script in your app source, so ran
26+
it to allow for customisation of the build process.
27+
28+
However, this script exited with a non-zero exit status.
29+
30+
Fix any errors output by your script above, or remove/rename
31+
the script to prevent it from being run during the build.
32+
EOF
33+
meta_set "failure_reason" "hooks::${hook_name}"
34+
exit 1
35+
fi
36+
37+
meta_time "${hook_name}_hook_duration" "${hook_start_time}"
38+
else
39+
meta_set "${hook_name}_hook" "false"
40+
fi
41+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
echo 'Some post_compile error!' >&2
6+
exit 1

spec/fixtures/hooks_failing_post_compile/requirements.txt

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
echo 'Some pre_compile error!' >&2
6+
exit 1

spec/fixtures/hooks_failing_pre_compile/requirements.txt

Whitespace-only changes.

spec/hatchet/ci_spec.rb

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,20 @@
2323
.*
2424
Successfully installed .* pytest-8.3.3
2525
-----> Skipping Django collectstatic since the env var DISABLE_COLLECTSTATIC is set.
26-
-----> Running post-compile hook
27-
CI=true
28-
CPLUS_INCLUDE_PATH=/app/.heroku/python/include
29-
C_INCLUDE_PATH=/app/.heroku/python/include
30-
DISABLE_COLLECTSTATIC=1
31-
INSTALL_TEST=1
32-
LANG=en_US.UTF-8
33-
LC_ALL=C.UTF-8
34-
LD_LIBRARY_PATH=/app/.heroku/python/lib
35-
LIBRARY_PATH=/app/.heroku/python/lib
36-
PATH=/app/.heroku/python/bin::/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/app/.sprettur/bin/
37-
PIP_NO_PYTHON_VERSION_WARNING=1
38-
PKG_CONFIG_PATH=/app/.heroku/python/lib/pkg-config
39-
PYTHONUNBUFFERED=1
26+
-----> Running bin/post_compile hook
27+
CI=true
28+
CPLUS_INCLUDE_PATH=/app/.heroku/python/include
29+
C_INCLUDE_PATH=/app/.heroku/python/include
30+
DISABLE_COLLECTSTATIC=1
31+
INSTALL_TEST=1
32+
LANG=en_US.UTF-8
33+
LC_ALL=C.UTF-8
34+
LD_LIBRARY_PATH=/app/.heroku/python/lib
35+
LIBRARY_PATH=/app/.heroku/python/lib
36+
PATH=/app/.heroku/python/bin::/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/app/.sprettur/bin/
37+
PIP_NO_PYTHON_VERSION_WARNING=1
38+
PKG_CONFIG_PATH=/app/.heroku/python/lib/pkg-config
39+
PYTHONUNBUFFERED=1
4040
-----> Inline app detected
4141
LANG=en_US.UTF-8
4242
LD_LIBRARY_PATH=/app/.heroku/python/lib
@@ -77,7 +77,7 @@
7777
-----> Installing requirements with pip
7878
-----> Installing test dependencies...
7979
-----> Skipping Django collectstatic since the env var DISABLE_COLLECTSTATIC is set.
80-
-----> Running post-compile hook
80+
-----> Running bin/post_compile hook
8181
OUTPUT
8282
end
8383
end
@@ -99,20 +99,20 @@
9999
Installing dependencies from Pipfile.lock \\(.+\\)...
100100
Installing dependencies from Pipfile.lock \\(.+\\)...
101101
-----> Skipping Django collectstatic since the env var DISABLE_COLLECTSTATIC is set.
102-
-----> Running post-compile hook
103-
CI=true
104-
CPLUS_INCLUDE_PATH=/app/.heroku/python/include
105-
C_INCLUDE_PATH=/app/.heroku/python/include
106-
DISABLE_COLLECTSTATIC=1
107-
INSTALL_TEST=1
108-
LANG=en_US.UTF-8
109-
LC_ALL=C.UTF-8
110-
LD_LIBRARY_PATH=/app/.heroku/python/lib
111-
LIBRARY_PATH=/app/.heroku/python/lib
112-
PATH=/app/.heroku/python/bin::/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/app/.sprettur/bin/
113-
PIP_NO_PYTHON_VERSION_WARNING=1
114-
PKG_CONFIG_PATH=/app/.heroku/python/lib/pkg-config
115-
PYTHONUNBUFFERED=1
102+
-----> Running bin/post_compile hook
103+
CI=true
104+
CPLUS_INCLUDE_PATH=/app/.heroku/python/include
105+
C_INCLUDE_PATH=/app/.heroku/python/include
106+
DISABLE_COLLECTSTATIC=1
107+
INSTALL_TEST=1
108+
LANG=en_US.UTF-8
109+
LC_ALL=C.UTF-8
110+
LD_LIBRARY_PATH=/app/.heroku/python/lib
111+
LIBRARY_PATH=/app/.heroku/python/lib
112+
PATH=/app/.heroku/python/bin::/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/app/.sprettur/bin/
113+
PIP_NO_PYTHON_VERSION_WARNING=1
114+
PKG_CONFIG_PATH=/app/.heroku/python/lib/pkg-config
115+
PYTHONUNBUFFERED=1
116116
-----> Inline app detected
117117
LANG=en_US.UTF-8
118118
LD_LIBRARY_PATH=/app/.heroku/python/lib
@@ -154,7 +154,7 @@
154154
Installing dependencies from Pipfile.lock \\(.+\\)...
155155
Installing dependencies from Pipfile.lock \\(.+\\)...
156156
-----> Skipping Django collectstatic since the env var DISABLE_COLLECTSTATIC is set.
157-
-----> Running post-compile hook
157+
-----> Running bin/post_compile hook
158158
REGEX
159159
end
160160
end

0 commit comments

Comments
 (0)