From 08dcc822ece08c72805b18cbd663a4f7fecbf726 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 12 Jun 2026 08:51:15 +0300 Subject: Guard template render setup failures for bm0 --- bin/shuriken | 75 +++++++++++++++++++----- src/lib/template.source.sh | 75 +++++++++++++++++++----- tests/cli.sh | 140 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 26 deletions(-) diff --git a/bin/shuriken b/bin/shuriken index 7ed6069..82c84db 100755 --- a/bin/shuriken +++ b/bin/shuriken @@ -724,11 +724,16 @@ template_required_context_vars() { validate_template_context() { local -r template_name="$1"; shift local -r context_name="$1"; shift + local -i status=0 local -a required_vars=() template_required_context_vars_to required_vars "$template_name" require_template_context_vars "$template_name" "$context_name" \ - "${required_vars[@]}" || return + "${required_vars[@]}" + status=$? + if (( status != 0 )); then + return "$status" + fi } source_template_file() { @@ -739,19 +744,28 @@ source_template_file() { local -i status=0 context_file=$(mktemp) + status=$? + if (( status != 0 )); then + return "$status" + fi + { serialize_template_render_context "$render_vars_name" printf 'unset BASH_ENV\n' } > "$context_file" - - if env -i PATH="$PATH" BASH_ENV="$context_file" \ - bash -euo pipefail -- "$template_path" >> "$output_path"; then - rm -f "$context_file" - else - status=$? + status=$? + if (( status != 0 )); then rm -f "$context_file" return "$status" fi + + env -i PATH="$PATH" BASH_ENV="$context_file" \ + bash -euo pipefail -- "$template_path" >> "$output_path" + status=$? + rm -f "$context_file" + if (( status != 0 )); then + return "$status" + fi } parse_template_context() { @@ -913,13 +927,18 @@ validate_template_render_request() { local -r template_name="$1"; shift local -r context_name="$1"; shift local -r template_path="$TEMPLATE_DIR/$template_name.tmpl" + local -i status=0 if [ ! -r "$template_path" ]; then config_error "template file $template_path must be readable" return 1 fi - validate_template_context "$template_name" "$context_name" || return + validate_template_context "$template_name" "$context_name" + status=$? + if (( status != 0 )); then + return "$status" + fi } render_template() { @@ -929,6 +948,7 @@ render_template() { local -r template_path="$TEMPLATE_DIR/$template_name.tmpl" local dist_html local html_dir + local -i status=0 # Passed by name to prepare_template_render_vars and source_template_file. # shellcheck disable=SC2034 local -A render_vars=() @@ -940,19 +960,48 @@ render_template() { "Rendering $template_name template into $(_display_path "$dist_html")/$html" mkdir -p "$dist_html" - prepare_template_render_vars render_vars "$context_name" || return - source_template_file "$template_path" "$dist_html/$html" render_vars || return + status=$? + if (( status != 0 )); then + return "$status" + fi + + prepare_template_render_vars render_vars "$context_name" + status=$? + if (( status != 0 )); then + return "$status" + fi + + source_template_file "$template_path" "$dist_html/$html" render_vars + status=$? + if (( status != 0 )); then + return "$status" + fi } template() { local -r template_name="$1"; shift local -r html="$1"; shift + local -i status=0 # shellcheck disable=SC2034 local -A render_context=() - parse_template_context "$template_name" render_context "$@" || return - validate_template_render_request "$template_name" render_context || return - render_template "$template_name" "$html" render_context || return + parse_template_context "$template_name" render_context "$@" + status=$? + if (( status != 0 )); then + return "$status" + fi + + validate_template_render_request "$template_name" render_context + status=$? + if (( status != 0 )); then + return "$status" + fi + + render_template "$template_name" "$html" render_context + status=$? + if (( status != 0 )); then + return "$status" + fi } # Inlined from src/lib/image.source.sh diff --git a/src/lib/template.source.sh b/src/lib/template.source.sh index 776d25a..93b8187 100644 --- a/src/lib/template.source.sh +++ b/src/lib/template.source.sh @@ -279,11 +279,16 @@ template_required_context_vars() { validate_template_context() { local -r template_name="$1"; shift local -r context_name="$1"; shift + local -i status=0 local -a required_vars=() template_required_context_vars_to required_vars "$template_name" require_template_context_vars "$template_name" "$context_name" \ - "${required_vars[@]}" || return + "${required_vars[@]}" + status=$? + if (( status != 0 )); then + return "$status" + fi } source_template_file() { @@ -294,19 +299,28 @@ source_template_file() { local -i status=0 context_file=$(mktemp) + status=$? + if (( status != 0 )); then + return "$status" + fi + { serialize_template_render_context "$render_vars_name" printf 'unset BASH_ENV\n' } > "$context_file" - - if env -i PATH="$PATH" BASH_ENV="$context_file" \ - bash -euo pipefail -- "$template_path" >> "$output_path"; then - rm -f "$context_file" - else - status=$? + status=$? + if (( status != 0 )); then rm -f "$context_file" return "$status" fi + + env -i PATH="$PATH" BASH_ENV="$context_file" \ + bash -euo pipefail -- "$template_path" >> "$output_path" + status=$? + rm -f "$context_file" + if (( status != 0 )); then + return "$status" + fi } parse_template_context() { @@ -468,13 +482,18 @@ validate_template_render_request() { local -r template_name="$1"; shift local -r context_name="$1"; shift local -r template_path="$TEMPLATE_DIR/$template_name.tmpl" + local -i status=0 if [ ! -r "$template_path" ]; then config_error "template file $template_path must be readable" return 1 fi - validate_template_context "$template_name" "$context_name" || return + validate_template_context "$template_name" "$context_name" + status=$? + if (( status != 0 )); then + return "$status" + fi } render_template() { @@ -484,6 +503,7 @@ render_template() { local -r template_path="$TEMPLATE_DIR/$template_name.tmpl" local dist_html local html_dir + local -i status=0 # Passed by name to prepare_template_render_vars and source_template_file. # shellcheck disable=SC2034 local -A render_vars=() @@ -495,17 +515,46 @@ render_template() { "Rendering $template_name template into $(_display_path "$dist_html")/$html" mkdir -p "$dist_html" - prepare_template_render_vars render_vars "$context_name" || return - source_template_file "$template_path" "$dist_html/$html" render_vars || return + status=$? + if (( status != 0 )); then + return "$status" + fi + + prepare_template_render_vars render_vars "$context_name" + status=$? + if (( status != 0 )); then + return "$status" + fi + + source_template_file "$template_path" "$dist_html/$html" render_vars + status=$? + if (( status != 0 )); then + return "$status" + fi } template() { local -r template_name="$1"; shift local -r html="$1"; shift + local -i status=0 # shellcheck disable=SC2034 local -A render_context=() - parse_template_context "$template_name" render_context "$@" || return - validate_template_render_request "$template_name" render_context || return - render_template "$template_name" "$html" render_context || return + parse_template_context "$template_name" render_context "$@" + status=$? + if (( status != 0 )); then + return "$status" + fi + + validate_template_render_request "$template_name" render_context + status=$? + if (( status != 0 )); then + return "$status" + fi + + render_template "$template_name" "$html" render_context + status=$? + if (( status != 0 )); then + return "$status" + fi } diff --git a/tests/cli.sh b/tests/cli.sh index 89947e0..311cff9 100755 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -2075,6 +2075,60 @@ BASH test::teardown } +test_generate_action_runs_with_errexit_active() { + local config_file + local output + local ran_file + local -i status=0 + + test::setup + config_file="$TEST_TMPDIR/shuriken.conf" + ran_file="$TEST_TMPDIR/dry-run-continued" + mkdir -p "$TEST_TMPDIR/incoming" + test::write_preflight_config \ + "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \ + "$TEST_REPO_ROOT/share/templates/default" + + set +e + output=$( + bash -euo pipefail -s "$TEST_SHURIKEN" "$config_file" "$ran_file" \ + 2>&1 \ + <<'BASH' +shuriken="$1"; shift +config_file="$1"; shift +ran_file="$1"; shift + +# shellcheck source=/dev/null +source <(sed '$d' "$shuriken") + +dry_run() { + false + printf 'dry_run continued\n' > "$ran_file" +} + +SHURIKEN_CLI_ACTION=--dry-run +SHURIKEN_CLI_CONFIG_FILE="$config_file" +SHURIKEN_CLI_HAS_CONFIG_OVERRIDES=no +SHURIKEN_CLI_OVERRIDES=() +SHURIKEN_CLI_SYNC_DESTINATIONS=() +SHURIKEN_FORCE_GENERATE=no + +run_configured_action +BASH + ) + status=$? + set -e + + if (( status == 0 )); then + printf 'FAIL: expected run_configured_action to fail\n' >&2 + printf '%s\n' "$output" >&2 + exit 1 + fi + + test::assert_path_absent "$ran_file" + test::teardown +} + test_generate_preflight_accepts_empty_height() { local config_file local fake_bin @@ -3274,6 +3328,86 @@ test_template_context_validator_fails_fast_without_errexit() { "$output" } +test_template_mktemp_failure_does_not_render_without_errexit() { + local fake_bin + local output + local output_file + local ran_file + local template_dir + local -i status=0 + + test::setup + fake_bin="$TEST_TMPDIR/bin" + template_dir="$TEST_TMPDIR/templates" + output_file="$TEST_TMPDIR/dist/out.html" + ran_file="$TEST_TMPDIR/template-ran" + mkdir -p "$fake_bin" "$template_dir" "$TEST_TMPDIR/dist" + { + printf '#!/usr/bin/env bash\n' + printf 'exit 42\n' + } > "$fake_bin/mktemp" + chmod 0755 "$fake_bin/mktemp" + { + printf 'printf rendered >> %q\n' "$ran_file" + printf 'printf rendered\n' + } > "$template_dir/preview.tmpl" + + set +e + output=$( + bash -euo pipefail -s \ + "$TEST_SHURIKEN" \ + "$fake_bin" \ + "$template_dir" \ + "$TEST_TMPDIR/dist" \ + 2>&1 \ + <<'BASH' +shuriken="$1"; shift +fake_bin="$1"; shift +template_dir="$1"; shift +dist_dir="$1"; shift + +# shellcheck source=/dev/null +source <(sed '$d' "$shuriken") + +PATH="$fake_bin:$PATH" +DIST_DIR="$dist_dir" +TEMPLATE_DIR="$template_dir" +TITLE='Template mktemp failure' +HEIGHT='' +THUMBHEIGHT=30 +MAXPREVIEWS=40 +ORIGINAL_BASEPATH='' +TARBALL_INCLUDE=no +SHURIKEN_OUTPUT_MODE=quiet + +template preview out.html \ + animation_class '' \ + backhref '#' \ + html_dir . \ + page_num 1 \ + photo photo.jpg \ + preview_num 1 \ + thumbs_dir thumbs +BASH + ) + status=$? + set -e + + if (( status == 0 )); then + printf 'FAIL: expected template rendering to fail\n' >&2 + printf '%s\n' "$output" >&2 + exit 1 + fi + + test::assert_path_absent "$ran_file" + if [ -s "$output_file" ]; then + printf 'FAIL: expected template output to stay empty\n' >&2 + cat "$output_file" >&2 + exit 1 + fi + test::teardown +} + test_generate_swap_failure_restores_dist() { local config_file local fake_bin @@ -4090,6 +4224,9 @@ main() { test::run_case \ '--generate validation failure skips action without errexit' \ test_generate_validation_failure_skips_action_without_errexit + test::run_case \ + '--generate action runs with errexit active' \ + test_generate_action_runs_with_errexit_active test::run_case \ '--generate preflight accepts empty HEIGHT' \ test_generate_preflight_accepts_empty_height @@ -4174,6 +4311,9 @@ main() { test::run_case \ 'template context validator fails fast without errexit' \ test_template_context_validator_fails_fast_without_errexit + test::run_case \ + 'template mktemp failure does not render without errexit' \ + test_template_mktemp_failure_does_not_render_without_errexit test::run_case \ '--generate swap failure restores final dist' \ test_generate_swap_failure_restores_dist -- cgit v1.2.3