testmark-bash

Refuse bash older than 4.4 with one clear error when sourced.

commit 703211b0e1a33c252c7d15380d76ffdabfe83ffb · parent 7be6f95327 · browse this tree
Ximianth, 2026-10-23 13:35:03 UTC

Before, macOS's /bin/bash (3.2) printed "declare: -g: invalid option" three times
and sourcing still succeeded; the failures came later, from inside testmark_parse.
The check is in syntax 3.2 runs, and makes `source` return 1.

tests/test-old-bash.sh runs the check under bash 3.2 and 4.3 builds, if present,
and skips each missing one with a note.
diff --git a/lib/testmark.bash b/lib/testmark.bash
index c747d56..5e80e44 100644
--- a/lib/testmark.bash
+++ b/lib/testmark.bash
@@ -1,10 +1,17 @@
 ## testmark.bash: read testmark documents (data hunks embedded in markdown) from bash.
 ##
 ## Source this file, by a path containing a slash; it sources the rest of the library from beside itself.
+## Needs bash 4.4 or later; on older ones, sourcing fails.
 ## Every public function is a shim over a private `_testmark_*` implementation, run through `_testmark_call`.
 ## Data comes out on stdout as exact bytes; errors go to stderr as "testmark: ...", with a nonzero status.
 ## Documents are addressed by a handle the caller picks at parse time; handles match `[A-Za-z0-9_]+`.
 
+# Checked first, in syntax bash 3.2 (macOS's /bin/bash) still runs, so an old bash gets one clear error.
+if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4))); then
+	>&2 echo "testmark: needs bash 4.4 or later; this is $BASH_VERSION"
+	return 1
+fi
+
 # shellcheck source=lib/testmark/document.bash
 source "${BASH_SOURCE[0]%/*}/testmark/document.bash"
 
diff --git a/tests/test-old-bash.sh b/tests/test-old-bash.sh
new file mode 100755
index 0000000..22547d1
--- /dev/null
+++ b/tests/test-old-bash.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+## Sourcing under a bash older than 4.4 fails with one clear line.
+## Uses the builds in BASH_VERSIONS_DIR (default /tmp/bash-versions), from the research's build-bash-versions.sh;
+## each one that's missing is skipped with a note.
+set -euo pipefail
+cd "$(dirname "$0")"
+# shellcheck source=tests/lib.sh
+source ./lib.sh
+
+for v in 3.2 4.3; do
+	b=${BASH_VERSIONS_DIR:-/tmp/bash-versions}/bin/bash-$v
+	if [ ! -x "$b" ]; then
+		echo "SKIP  bash $v: no $b"
+		continue
+	fi
+	got=$("$b" -c 'source "$1" 2>&1 || echo "rc=$?"' _ "$ROOT/lib/testmark.bash")
+	expect_eq "bash $v refuses" "$got" "testmark: needs bash 4.4 or later; this is $("$b" -c 'echo "$BASH_VERSION"')"$'\n'"rc=1"
+done
+
+exit "$RESULT"