From 4e59de1adb1286d3360429ece95691fc6bda6103 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:28:50 +0200 Subject: [PATCH] Report a handled silent failure only on its own line After `$STD cmd || true` the record and the .errinfo capture stayed until the next $STD call, so a later plain command's failure was reported as the handled one: Open WebUI's systemctl failures arrived in the telemetry as intel-basekit, exit 100. The record now counts for the line it came from; otherwise its capture is dropped so the container writes a fresh one for the command that actually failed. --- .github/workflows/verify-split-files.yml | 4 ++ core/error_handler.func | 16 ++++++-- tests/failed-command-output.sh | 5 ++- tests/stale-silent-record.sh | 49 ++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 tests/stale-silent-record.sh diff --git a/.github/workflows/verify-split-files.yml b/.github/workflows/verify-split-files.yml index 644866f..64c3850 100644 --- a/.github/workflows/verify-split-files.yml +++ b/.github/workflows/verify-split-files.yml @@ -39,6 +39,7 @@ on: - "tests/incus-target.sh" - "tests/incus-storage-space.sh" - "tests/release-stamp-pending.sh" + - "tests/stale-silent-record.sh" - ".github/workflows/verify-split-files.yml" push: branches: [main] @@ -69,6 +70,7 @@ on: - "tests/incus-target.sh" - "tests/incus-storage-space.sh" - "tests/release-stamp-pending.sh" + - "tests/stale-silent-record.sh" - ".github/workflows/verify-split-files.yml" workflow_dispatch: @@ -139,6 +141,8 @@ jobs: run: bash tests/incus-storage-space.sh - name: Release stamp pending marker tests run: bash tests/release-stamp-pending.sh + - name: Stale silent record tests + run: bash tests/stale-silent-record.sh - name: Prefetch lists cover every engine file run: | diff --git a/core/error_handler.func b/core/error_handler.func index a79a367..c434404 100644 --- a/core/error_handler.func +++ b/core/error_handler.func @@ -665,12 +665,20 @@ error_handler() { command="${command//\$STD/}" # silent() knows the real failing command; the trap only sees the wrapper. + # Its record outlives a failure the caller handled, so it only counts for + # the line it came from; otherwise its capture would be sent for this one. local from_silent=0 if [[ -n "${_SILENT_FAILED_RC:-}" ]]; then - exit_code="$_SILENT_FAILED_RC" - command="$_SILENT_FAILED_CMD" - line_number="$_SILENT_FAILED_LINE" - from_silent=1 + if [[ "${_SILENT_FAILED_LINE:-}" == "$line_number" ]]; then + exit_code="$_SILENT_FAILED_RC" + command="$_SILENT_FAILED_CMD" + from_silent=1 + else + if [[ -n "${_SILENT_FAILED_LOG:-}" ]]; then + rm -f "${_SILENT_FAILED_LOG}.errinfo" 2>/dev/null + fi + unset _SILENT_FAILED_LOG + fi unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE fi diff --git a/tests/failed-command-output.sh b/tests/failed-command-output.sh index adbb722..163eefd 100644 --- a/tests/failed-command-output.sh +++ b/tests/failed-command-output.sh @@ -99,8 +99,9 @@ msg_error() { echo "error: $*"; } handler_output() { # error_handler exits, so it runs in a subshell with the state silent() leaves behind. ( - export _SILENT_FAILED_RC=1 _SILENT_FAILED_CMD="pnpm install" _SILENT_FAILED_LINE=34 _SILENT_FAILED_LOG="$LOGFILE" - error_handler 1 "silent pnpm install" 2>&1 + export _SILENT_FAILED_RC=1 _SILENT_FAILED_CMD="pnpm install" _SILENT_FAILED_LOG="$LOGFILE" + # The record only counts for the line it came from. + _SILENT_FAILED_LINE=$LINENO error_handler 1 "silent pnpm install" 2>&1 ) } reset diff --git a/tests/stale-silent-record.sh b/tests/stale-silent-record.sh new file mode 100644 index 0000000..fb6e5e8 --- /dev/null +++ b/tests/stale-silent-record.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# silent() leaves its record for the ERR trap. After `$STD cmd || true` the +# record and its .errinfo stayed until the next $STD call, so a later plain +# command's failure was reported as the handled one. +set -uo pipefail + +CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +eval "$(awk '/^error_handler[(][)] [{]/,/^}/' "$CORE_DIR/core/error_handler.func")" + +TEST_DIR="$(mktemp -d)" +trap 'rm -rf -- "$TEST_DIR"' EXIT +LOG="$TEST_DIR/install.log" +ERRINFO="$LOG.errinfo" +passed=0 failed=0 + +_eh_explanation() { echo "explained"; } +_eh_report() { + printf 'REPORT %s|%s|%s\n' "$1" "$2" "$3" + exit 0 +} + +expect() { + if [[ "$2" == "$3" ]]; then + echo "PASS $1" + passed=$((passed + 1)) + else + echo "FAIL $1: expected <$2>, got <$3>" >&2 + failed=$((failed + 1)) + fi +} +handled_failure() { + echo "E: Unable to locate package intel-basekit" >"$LOG" + printf '%s\n' 'EXIT_CODE=100' 'LINE=5' 'COMMAND=apt install -y intel-basekit' '--- OUTPUT ---' 'E: Unable to locate package intel-basekit' >"$ERRINFO" + export _SILENT_FAILED_RC=100 _SILENT_FAILED_CMD='apt install -y intel-basekit' _SILENT_FAILED_LINE="$1" _SILENT_FAILED_LOG="$LOG" +} + +# A later failure on another line is its own. +handled_failure 5 +out="$(error_handler 1 'systemctl enable -q --now open-webui')" +expect "the later failure keeps its code and command" 1 "$(grep -c 'REPORT 1|systemctl enable -q --now open-webui|' <<<"$out")" +expect "the handled failure's capture is dropped" 0 "$(ls "$ERRINFO" 2>/dev/null | wc -l)" + +# On its own line the record is what the trap reports. +line=$LINENO; handled_failure "$line"; out="$(error_handler 1 'silent apt install -y intel-basekit')" +expect "silent's record is used for its own line" 1 "$(grep -c 'REPORT 100|apt install -y intel-basekit|' <<<"$out")" +expect "its capture is kept" 1 "$(ls "$ERRINFO" 2>/dev/null | wc -l)" + +echo "$passed passed, $failed failed" +((failed == 0))